Skip to content

Receiving Goods from Purchase Orders

Overview

The receiving process is a critical step in the purchase order lifecycle where incoming goods are verified, quality-checked, and added to inventory. This guide covers the complete receiving workflow, from full receipts to partial deliveries with quality control.

Key Concepts

Receipt Types

Full Receipt

  • All ordered items received in a single shipment
  • PO status changes from SENTRECEIVED
  • Complete inventory update in one transaction
  • Simplest and most common scenario

Partial Receipt

  • Only some items received from the PO
  • PO status changes from SENTPARTIALLY_RECEIVED
  • Can receive multiple partial shipments over time
  • Tracks pending quantities for outstanding items
  • Final partial receipt moves status to RECEIVED

Quality Control

The receiving process includes optional quality checks:

  • Pass: Items accepted and added to available inventory
  • Fail: Issues documented, supplier notified, possible return/replacement
  • Quality check results affect supplier performance ratings
  • Failed quality checks require notes explaining the issue

Business Rules

Receiving Constraints

  1. Status Requirements: Can only receive POs in these statuses:

    • SENT - Standard receiving path
    • ACKNOWLEDGED - Supplier confirmed, awaiting delivery
    • PARTIALLY_RECEIVED - Additional shipment for partial PO
  2. Quantity Validation:

    • Cannot receive more than ordered quantity
    • Cannot receive negative quantities
    • Received quantity must be at least 1
    • System prevents over-receiving to maintain order accuracy
  3. Quality Check Rules:

    • If quality check fails, quality notes are required
    • Failed quality checks recorded per line item
    • Overall PO quality status reflects worst line item result
    • Quality failures stored in internal notes for audit trail
  4. Inventory Updates:

    • Inventory updated automatically upon receipt
    • Stock movements created for audit trail
    • Warehouse specified in line item receives the stock
    • Reserved stock adjusted if needed

API Endpoint

Receive Purchase Order

Endpoint: POST /api/v1/operations/purchase-orders/{purchaseOrder}/receive

Authentication: Required (Bearer token)

Route Name: purchase-orders.receive

Request Structure

Full Receipt (All Items)

To receive all items at their full ordered quantities, send an empty request:

json
{
  "notes": "Delivery received in good condition",
  "quality_check_passed": true
}

Parameters:

  • notes (optional, string, max 2000): General receiving notes
  • quality_check_passed (optional, boolean, default: true): Overall quality status
  • quality_notes (optional, string, max 2000): Required if quality_check_passed = false
  • received_date (optional, date, Y-m-d): Date received (defaults to today)

Partial Receipt (Specific Items)

To receive specific items with custom quantities:

json
{
  "received_items": [
    {
      "product_id": 101,
      "quantity_received": 50,
      "notes": "First shipment - 50 units received",
      "quality_check_passed": true
    },
    {
      "product_id": 102,
      "quantity_received": 25,
      "quality_check_passed": false,
      "quality_notes": "5 units damaged during shipping - supplier notified"
    }
  ],
  "notes": "Partial delivery - remaining items expected next week",
  "quality_check_passed": false,
  "quality_notes": "Some damaged items in shipment - see line item notes"
}

received_items Array Parameters:

  • product_id (required, integer): Product being received
  • quantity_received (required, integer, 1-1000000): Quantity received
  • warehouse_id (optional, integer): Warehouse if PO has multiple warehouses
  • notes (optional, string, max 500): Line item specific notes
  • quality_check_passed (optional, boolean): Quality check for this item
  • quality_notes (optional, string, max 1000): Required if quality check failed

Overall Parameters:

  • notes (optional, string, max 2000): General receipt notes
  • quality_check_passed (optional, boolean): Overall quality status
  • quality_notes (optional, string, max 2000): Overall quality notes
  • received_date (optional, date): Receipt date (Y-m-d format)

Response Structure

Success Response (200)

json
{
  "message": "Purchase order marked as received successfully",
  "data": {
    "id": 45,
    "order_number": "PO-202512-00045",
    "status": "received",
    "supplier": {
      "id": 12,
      "name": "Acme Supplies Inc",
      "email": "orders@acmesupplies.com"
    },
    "total_amount": 2450.00,
    "currency": "USD",
    "order_date": "2025-12-01",
    "expected_delivery_date": "2025-12-15",
    "actual_delivery_date": "2025-12-17",
    "internal_notes": "Receipt notes: Delivery received in good condition",
    "line_items": [
      {
        "id": 156,
        "product_id": 101,
        "product_sku": "WIDGET-001",
        "product_name": "Premium Widget",
        "quantity_ordered": 100,
        "quantity_received": 100,
        "unit_price": 15.50,
        "line_total": 1550.00,
        "receiving_notes": "All units received in good condition",
        "received_at": "2025-12-17T14:30:00Z"
      },
      {
        "id": 157,
        "product_id": 102,
        "product_sku": "GADGET-002",
        "product_name": "Smart Gadget",
        "quantity_ordered": 50,
        "quantity_received": 50,
        "unit_price": 18.00,
        "line_total": 900.00,
        "receiving_notes": null,
        "received_at": "2025-12-17T14:30:00Z"
      }
    ]
  }
}

Error Response - Invalid Status (422)

json
{
  "error": "Failed to receive purchase order",
  "message": "Cannot receive purchase order in status: draft. Order must be SENT, ACKNOWLEDGED, or PARTIALLY_RECEIVED."
}

Error Response - Product Not Found (422)

json
{
  "error": "Product not found in purchase order",
  "message": "Product ID 999 is not in this purchase order"
}

Error Response - Over-Receiving (500)

json
{
  "error": "Failed to receive purchase order",
  "message": "Cannot receive 150 units. Only 100 units pending (ordered: 100, received: 0)"
}

Workflow Examples

Example 1: Full Receipt - Simple Delivery

Scenario: All items arrive in one shipment, no issues

Request:

bash
curl -X POST https://api.example.com/api/v1/operations/purchase-orders/45/receive \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "notes": "Delivery received on time, all items in good condition",
    "quality_check_passed": true
  }'

What Happens:

  1. System validates PO status (must be SENT/ACKNOWLEDGED)
  2. All line items received at full ordered quantities
  3. Quality check recorded as passed
  4. Inventory updated for all products
  5. Stock movements created for audit trail
  6. PO status changes to RECEIVED
  7. Actual delivery date set to today
  8. Supplier performance metrics updated (on-time delivery)

Example 2: Partial Receipt - Split Shipment

Scenario: Supplier ships in two batches, first batch arrives

First Shipment Request:

json
{
  "received_items": [
    {
      "product_id": 101,
      "quantity_received": 50,
      "notes": "First batch - 50 of 100 units"
    }
  ],
  "notes": "First shipment received - expecting second shipment Dec 20",
  "quality_check_passed": true
}

What Happens:

  1. Product 101: 50 units received (50 pending)
  2. Product 102: 0 units received (50 still pending)
  3. PO status changes to PARTIALLY_RECEIVED
  4. Inventory updated for 50 units of Product 101
  5. Stock movement created for partial receipt
  6. Remaining quantities tracked for next shipment

Second Shipment Request (3 days later):

json
{
  "received_items": [
    {
      "product_id": 101,
      "quantity_received": 50,
      "notes": "Second batch - completing order"
    },
    {
      "product_id": 102,
      "quantity_received": 50,
      "notes": "All units of gadget received"
    }
  ],
  "notes": "Final shipment - order complete",
  "quality_check_passed": true
}

What Happens:

  1. Product 101: 50 more units received (total 100, fully received)
  2. Product 102: 50 units received (total 50, fully received)
  3. All items now fully received
  4. PO status changes to RECEIVED
  5. Inventory updated for remaining quantities
  6. Actual delivery date set to final shipment date

Example 3: Quality Issues - Damaged Goods

Scenario: Some items damaged during shipping

Request:

json
{
  "received_items": [
    {
      "product_id": 101,
      "quantity_received": 95,
      "quality_check_passed": false,
      "quality_notes": "5 units damaged - boxes crushed. Photos taken and emailed to supplier."
    },
    {
      "product_id": 102,
      "quantity_received": 50,
      "quality_check_passed": true,
      "notes": "All units in good condition"
    }
  ],
  "notes": "Partial damage on Product 101 - supplier contacted for replacement",
  "quality_check_passed": false,
  "quality_notes": "Overall shipment has quality issues - see line item details"
}

What Happens:

  1. Product 101: Only 95 units accepted (5 damaged)
  2. Quality failure recorded with detailed notes
  3. 5 units remain pending (awaiting replacement from supplier)
  4. PO status changes to PARTIALLY_RECEIVED
  5. Internal notes updated with quality issues
  6. Supplier performance rating affected
  7. Quality notes preserved for supplier communication

Follow-up for Replacement:

json
{
  "received_items": [
    {
      "product_id": 101,
      "quantity_received": 5,
      "notes": "Replacement units for damaged items received"
    }
  ],
  "notes": "Replacement shipment received - order now complete"
}

Example 4: Multi-Warehouse Receipt

Scenario: PO has items going to different warehouses

Request:

json
{
  "received_items": [
    {
      "product_id": 101,
      "warehouse_id": 1,
      "quantity_received": 50,
      "notes": "Received at Main Warehouse"
    },
    {
      "product_id": 101,
      "warehouse_id": 2,
      "quantity_received": 50,
      "notes": "Received at Regional Warehouse"
    }
  ],
  "notes": "Split shipment to two warehouse locations"
}

What Happens:

  1. 50 units added to inventory at Warehouse 1
  2. 50 units added to inventory at Warehouse 2
  3. Separate stock movements created for each warehouse
  4. Each warehouse's inventory updated independently

Validation Rules

Field Validation

received_items.*.product_id

  • Required when received_items specified
  • Must be integer
  • Must exist in the purchase order line items

received_items.*.quantity_received

  • Required when received_items specified
  • Must be integer between 1 and 1,000,000
  • Cannot exceed pending quantity for that line item

received_items.*.notes

  • Optional
  • String, max 500 characters

received_items.*.quality_check_passed

  • Optional boolean
  • Defaults to true

received_items.*.quality_notes

  • Optional string, max 1000 characters
  • Required if quality_check_passed = false

notes

  • Optional string, max 2000 characters
  • Appended to PO internal_notes field

quality_check_passed

  • Optional boolean
  • Defaults to true
  • Overall quality status for entire receipt

quality_notes

  • Optional string, max 2000 characters
  • Required if quality_check_passed = false AND no line item has quality_notes

received_date

  • Optional date in Y-m-d format
  • Defaults to current date
  • Cannot be in the future

Business Logic Validation

PO Status Check

Valid statuses: SENT, ACKNOWLEDGED, PARTIALLY_RECEIVED
Invalid: DRAFT, PENDING, APPROVED, RECEIVED, COMPLETED, CANCELLED

Quantity Validation

For each line item:
  pending_quantity = quantity_ordered - quantity_received

  if quantity_received > pending_quantity:
    reject with error

Quality Check Validation

if quality_check_passed == false:
  if quality_notes is empty AND no line items have quality_notes:
    reject with error

Backend Processing Flow

When you submit a receive request, the system performs these steps:

1. Request Validation

  • Validates PO exists and user has permission
  • Checks PO status allows receiving
  • Validates all input fields per rules above

2. Line Item Mapping

If received_items is empty:

  • System automatically receives all items at full pending quantities
  • For each line item, calculates: quantityToReceive = quantity_ordered - quantity_received
  • Only processes items with pending quantities > 0

If received_items is specified:

  • Maps product_id (and optionally warehouse_id) to specific line items
  • Validates each product exists in the PO
  • Uses specified quantities for each item

3. Domain Service Validation

The system validates:

  • Quantities don't exceed pending amounts
  • All specified line items exist in PO
  • Receipt quantities are within acceptable ranges
  • Quality check requirements are met

4. Receipt Processing

For each received item:

  • Updates quantity_received by adding the new quantity
  • Records received_at timestamp
  • Stores receiving_notes from the request
  • Saves quality check results if provided

5. Status Determination

The system calculates totals and determines new status:

If all items fully received:

  • Status changes to RECEIVED
  • Sets actual_delivery_date to received_date (or today)

If some items received:

  • Status changes to PARTIALLY_RECEIVED
  • Tracks pending quantities for outstanding items

If no items received:

  • Status remains SENT (unchanged)

6. Inventory Updates

For each received item:

  • Retrieves inventory record for the product and warehouse
  • Adjusts stock quantity by the received amount
  • Creates stock movement record with:
    • Movement type: "purchase"
    • Reason: "PO #{order_number} received"
    • Reference to the purchase order
  • Updates inventory totals and last movement timestamp

7. Event Dispatching

The system dispatches a PurchaseOrderReceived event containing:

  • Purchase order ID and order number
  • Received items and quantities
  • User who processed the receipt
  • Whether it's a partial receipt

This event triggers:

  • Email notifications to procurement team
  • Supplier performance metric updates
  • Inventory alerts if reorder points reached
  • Financial accrual updates (if Finance module integrated)

Integration Points

Inventory Module

Automatic Stock Updates

  • Each received item increases inventory.quantity_on_hand
  • Stock movements created for audit trail
  • Movement type: "purchase"
  • Reference links to PO and line item

Low Stock Alerts

  • After receiving, system checks if other products below reorder point
  • May trigger auto-reorder for different products

Supplier Module

Performance Metrics

  • On-time delivery rate calculated
  • Quality acceptance rate updated
  • Lead time variance recorded
  • Supplier rating adjusted based on quality checks

Finance Module (Future)

  • Receipt triggers invoice matching workflow
  • Accrual accounting for received goods
  • Three-way match: PO + Receipt + Invoice

Best Practices

Receiving Workflow

  1. Receive Promptly: Process receipts same-day to maintain accurate inventory
  2. Inspect Thoroughly: Quality check before accepting
  3. Document Issues: Take photos of damaged goods, detailed notes
  4. Partial Receipts OK: Don't wait for complete shipment
  5. Update Immediately: Real-time updates prevent stock discrepancies

Quality Control

  1. Always Inspect: Even from trusted suppliers
  2. Require Notes: Failed quality checks must explain issue
  3. Track Patterns: Repeated issues indicate supplier problems
  4. Communicate Fast: Notify supplier immediately of issues
  5. Photo Evidence: Pictures worth 1000 words in disputes

Data Entry

  1. Verify Product IDs: Ensure correct product being received
  2. Double-Check Quantities: Count carefully, errors compound
  3. Include Context: Notes help future troubleshooting
  4. Use Warehouse IDs: Multi-warehouse operations need clarity
  5. Date Accuracy: Correct dates for performance metrics

Handling Discrepancies

Over-Shipment (more than ordered):

  • System prevents over-receiving
  • Accept ordered quantity only
  • Coordinate with supplier for return/adjustment

Under-Shipment (less than ordered):

  • Receive actual quantity
  • Document shortage in notes
  • Contact supplier for ETA on remainder
  • Use partial receipt workflow

Wrong Items:

  • Don't receive incorrect items
  • Document issue with photos
  • Contact supplier immediately
  • Await correct items or cancellation

Damaged Items:

  • Receive good units only
  • Quality check failure with notes and photos
  • Document damage extent
  • Request replacement shipment

Troubleshooting

Common Errors

"Cannot receive purchase order in status: draft"

  • Cause: Trying to receive a PO that hasn't been sent
  • Solution: PO must be approved and sent first
  • Workflow: Draft → Approve → Send → Receive

"Product ID 999 is not in this purchase order"

  • Cause: product_id doesn't match any line item
  • Solution: Check PO details for correct product IDs
  • Tip: Use GET /purchase-orders/{id} to see line items

"Cannot receive 150 units. Only 100 units pending"

  • Cause: Trying to receive more than ordered
  • Solution: Check quantity_received vs quantity_ordered
  • Fix: Adjust quantity_received to match available amount

"Quality notes are required when quality check fails"

  • Cause: quality_check_passed = false but no notes provided
  • Solution: Add quality_notes explaining the failure
  • Rule: Failed quality checks must be documented

Validation Tips

Check PO Status First

bash
GET /api/v1/operations/purchase-orders/45

Verify status is SENT, ACKNOWLEDGED, or PARTIALLY_RECEIVED

Review Line Items

json
{
  "line_items": [
    {
      "id": 156,
      "product_id": 101,
      "quantity_ordered": 100,
      "quantity_received": 0,  // Shows pending quantity
      "warehouse_id": 1
    }
  ]
}

Calculate Pending Quantities

pending = quantity_ordered - quantity_received

Can only receive up to pending amount

Summary

The receiving process is the critical final step in the purchase order workflow. By following these guidelines, you can ensure accurate inventory, maintain quality standards, and build strong supplier relationships. Remember:

  • Receive promptly and inspect thoroughly
  • Document everything, especially issues
  • Use partial receipts for split shipments
  • Quality checks protect your operations
  • System prevents over-receiving for safety

Documentation for SynthesQ CRM/ERP Platform