Orders Module

The Orders module is the central hub of Auto Commerce. All order creation β€” from Shopify, WooCommerce, manual entry, CSV import, or API β€” flows through a single Ingest Pipeline. The module provides a full order lifecycle: payments, fulfillment, returns, invoicing, analytics, notifications, outbound webhooks, and bulk operations.

Architecture

StoreShopify / StoreWooCommerce / Manual / CSV / API
    β”‚
    Bus::call('Orders', 'orders.createOrUpdate', $normalized)
    β”‚
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚   OrderIngestService       β”‚
    β”‚   β€’ Validate & normalize   β”‚
    β”‚   β€’ Deduplicate (upsert)   β”‚
    β”‚   β€’ Create Order + Items   β”‚
    β”‚   β€’ Publish events         β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  β”‚
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚             β”‚              β”‚              β”‚
Workflows   ResellerOrders   Inventory    Notifications
(triggers)  (chain forward)  (reserve)    (SMS/WhatsApp)

Key Design Principles

  • Single entry point: All order creation goes through OrderIngestService, never direct Order::create()
  • Event-driven: 13 events published via Module Event Bus β€” other modules subscribe without coupling
  • Idempotent upsert: Deduplication via (source_module, external_id) constraint
  • PostgreSQL-optimized: JSONB columns, GIN indexes, BRIN indexes, partial indexes for hot statuses

Order Lifecycle

pending β†’ confirmed β†’ processing β†’ ready_to_ship β†’ shipped
  β†’ out_for_delivery β†’ delivered

Alternate flows:
  β†’ rto_initiated β†’ rto_received (Return to Origin)
  β†’ cancelled
  β†’ failed

Payment Statuses

pending β†’ paid β†’ refunded
       β†’ partial (partial payment received)
       β†’ failed
       β†’ cod (Cash on Delivery)

Data Model

Core Order Fields

Field Type Description
id UUID Primary key
external_id string ID from source platform (Shopify, etc.)
source_module string Origin module (store-shopify, manual, etc.)
order_number string Auto-generated (ORD-YYYYMMDD-XXXX)
status string Order lifecycle status
payment_status string Payment status
fulfillment_status string Fulfillment status
customer_id UUID Customer reference
subtotal decimal Items subtotal
tax decimal Total tax
discount decimal Total discount
shipping_cost decimal Shipping charges
total decimal Grand total
currency string Currency code (default: INR)
payment_method string Payment method (razorpay, cod, etc.)
notes text Internal notes
tags jsonb Tags array
custom_fields jsonb Tenant-defined custom field values

Related Models

Model Relationship Description
OrderItem HasMany Line items with product, quantity, price
OrderPayment HasMany Payment records with provider, amount, status
OrderRefund HasMany Refund records linked to payments
OrderReturn HasMany Return requests with lifecycle
OrderInvoice HasMany Tax invoices and credit notes
Shipment HasMany Shipment tracking with carrier integration
OrderStatusHistory HasMany Audit trail of status changes
OrderActivityLog HasMany Rich activity log with changes JSONB
OrderMetadata HasMany Key-value metadata from any module

Ingest Pipeline

The OrderIngestService is the single canonical entry point for all order creation.

Creating Orders via Bus

use App\Core\ModuleBus\ModuleApiBus;

$bus = app(ModuleApiBus::class);

// Simple create
$order = $bus->call('Orders', 'orders.create', [
    'customer' => ['phone' => '+919876543210', 'first_name' => 'John'],
    'items' => [
        ['name' => 'Widget', 'quantity' => 2, 'unit_price' => 999.00],
    ],
    'total' => 1998.00,
    'payment_method' => 'cod',
]);

// Idempotent upsert (for store integrations)
$order = $bus->call('Orders', 'orders.createOrUpdate', [
    'source_module' => 'store-shopify',
    'external_id' => 'shopify_12345',
    'customer' => ['email' => 'john@example.com', 'first_name' => 'John'],
    'items' => [...],
    'status' => 'confirmed',
    'payment_status' => 'paid',
    'total' => 2999.00,
]);

// Batch ingest
$result = $bus->call('Orders', 'orders.batchIngest', [
    'orders' => [$order1Data, $order2Data, ...],
    'source' => 'csv-import',
]);
// Returns: { created: 50, updated: 3, skipped: 0, errors: [...] }

How Shopify Integration Uses It

// In ShopifyService::importOrder()
$bus->call('Orders', 'orders.createOrUpdate', [
    'source_module' => 'store-shopify',
    'external_id' => (string) $shopifyOrder['id'],
    'order_number' => $shopifyOrder['name'],
    'customer' => [
        'email' => $shopifyOrder['email'],
        'phone' => $shopifyOrder['phone'],
        'first_name' => $shopifyOrder['customer']['first_name'] ?? 'Guest',
        'last_name' => $shopifyOrder['customer']['last_name'] ?? '',
    ],
    'items' => collect($shopifyOrder['line_items'])->map(fn($item) => [
        'name' => $item['title'],
        'sku' => $item['sku'],
        'quantity' => $item['quantity'],
        'unit_price' => $item['price'],
    ])->toArray(),
    'total' => $shopifyOrder['total_price'],
    'status' => $this->mapShopifyStatus($shopifyOrder),
    'payment_status' => $shopifyOrder['financial_status'] === 'paid' ? 'paid' : 'pending',
]);

API Endpoints

Orders CRUD

Method Endpoint Description
GET /api/v1/orders List orders (paginated, filterable)
POST /api/v1/orders Create order
GET /api/v1/orders/{order} Get order details
PATCH /api/v1/orders/{order} Update order
DELETE /api/v1/orders/{order} Delete order
PATCH /api/v1/orders/{order}/status Update status
GET /api/v1/orders/stats/summary Order statistics

Query Parameters for List:

Parameter Type Description
status string Filter by status
payment_status string Filter by payment status
customer_id UUID Filter by customer
source_module string Filter by source (shopify, manual, etc.)
date_from / date_to date Date range filter
search string Search order number, customer name/phone/email
page / per_page int Pagination

Payments

Method Endpoint Description
GET /api/v1/orders/{order}/payments List payments
POST /api/v1/orders/{order}/payments Record payment
POST /api/v1/orders/{order}/payments/link Generate payment link
POST /api/v1/orders/{order}/refund Process refund

Record Payment Request:

{
  "method": "razorpay",
  "amount": 2999.00,
  "external_payment_id": "pay_abc123",
  "metadata": { "gateway_response": "..." }
}

Shipments

Method Endpoint Description
POST /api/v1/orders/{order}/ship Create shipment
GET /api/v1/orders/{order}/shipments List shipments

Create Shipment Request:

{
  "carrier_module": "shipping-delhivery",
  "awb_number": "DLV123456789",
  "tracking_url": "https://track.delhivery.com/DLV123456789",
  "estimated_delivery_at": "2026-03-15T00:00:00Z"
}

Returns

Method Endpoint Description
GET /api/v1/orders/{order}/returns List order returns
POST /api/v1/orders/{order}/returns Request return
PATCH /api/v1/returns/{return}/approve Approve return
PATCH /api/v1/returns/{return}/reject Reject return
PATCH /api/v1/returns/{return}/received Mark as received
POST /api/v1/returns/{return}/refund Process refund for return

Request Return:

{
  "reason_category": "defective",
  "reason_detail": "Screen cracked on arrival",
  "items": [
    { "order_item_id": "uuid", "quantity": 1 }
  ]
}

Invoices

Method Endpoint Description
GET /api/v1/orders/{order}/invoices List invoices
POST /api/v1/orders/{order}/invoices Generate invoice
GET /api/v1/invoices/{invoice} Get invoice details

Analytics

Method Endpoint Description
GET /api/v1/orders/analytics/dashboard Dashboard metrics
GET /api/v1/orders/analytics/revenue Revenue time series
GET /api/v1/orders/analytics/fulfillment Fulfillment metrics
GET /api/v1/orders/analytics/sources Source breakdown
GET /api/v1/orders/analytics/products Top products
GET /api/v1/orders/analytics/cod-vs-prepaid COD vs Prepaid split

Dashboard Response:

{
  "total_orders": 1250,
  "total_revenue": 3750000.00,
  "avg_order_value": 3000.00,
  "pending_orders": 45,
  "orders_today": 28,
  "revenue_today": 84000.00,
  "fulfillment_rate": 0.89,
  "cancellation_rate": 0.05,
  "cod_percentage": 0.35,
  "prepaid_percentage": 0.65
}

Bulk Operations

Method Endpoint Description
POST /api/v1/orders/bulk/import Import orders from CSV
GET /api/v1/orders/bulk/import/{id} Import status
POST /api/v1/orders/bulk/import/{id}/cancel Cancel import
POST /api/v1/orders/bulk/export Export orders to CSV
GET /api/v1/orders/bulk/export/{id}/download Download export
POST /api/v1/orders/bulk/status Bulk update status
POST /api/v1/orders/bulk/ship Bulk create shipments
POST /api/v1/orders/bulk/cancel Bulk cancel orders

Bulk Import:

// POST /api/v1/orders/bulk/import
// Content-Type: multipart/form-data
{
  "file": "(csv file)",
  "source": "csv-import"
}

// Response
{
  "import_id": "uuid",
  "status": "processing",
  "total_rows": 500
}

Bulk Status Update:

{
  "order_ids": ["uuid1", "uuid2", "uuid3"],
  "status": "confirmed",
  "reason": "Payment verified in batch"
}

Notification Rules

Method Endpoint Description
GET /api/v1/order-notification-rules List rules
POST /api/v1/order-notification-rules Create rule
GET /api/v1/order-notification-rules/{rule} Get rule
PATCH /api/v1/order-notification-rules/{rule} Update rule
DELETE /api/v1/order-notification-rules/{rule} Delete rule
POST /api/v1/order-notification-rules/{rule}/test Send test notification

Create Notification Rule:

{
  "name": "Order Confirmed SMS",
  "event": "order.status_changed",
  "channel": "sms",
  "recipient_type": "customer",
  "template": "Hi {{customer_name}}, your order #{{order_number}} is confirmed! Total: β‚Ή{{total}}",
  "conditions": { "new_status": "confirmed" },
  "is_active": true
}

Outbound Webhooks

Method Endpoint Description
GET /api/v1/order-webhooks List webhook endpoints
POST /api/v1/order-webhooks Create endpoint
GET /api/v1/order-webhooks/{endpoint} Get endpoint
PATCH /api/v1/order-webhooks/{endpoint} Update endpoint
DELETE /api/v1/order-webhooks/{endpoint} Delete endpoint
GET /api/v1/order-webhooks/{endpoint}/logs View delivery logs

Create Webhook Endpoint:

{
  "url": "https://yourapp.com/webhook/orders",
  "secret": "whsec_abc123",
  "events": ["order.created", "order.status_changed", "order.delivered"],
  "is_active": true
}

Webhook Payload:

{
  "event": "order.status_changed",
  "payload": {
    "order_id": "uuid",
    "order_number": "ORD-20260305-A1B2",
    "old_status": "pending",
    "new_status": "confirmed"
  },
  "timestamp": "2026-03-05T10:30:00+00:00"
}

Webhooks include an X-Webhook-Signature header (HMAC-SHA256 of the body using the endpoint secret). Endpoints are automatically disabled after 10 consecutive failures.

Custom Fields

Method Endpoint Description
GET /api/v1/order-custom-fields List field definitions
POST /api/v1/order-custom-fields Create field
GET /api/v1/order-custom-fields/{field} Get field
PATCH /api/v1/order-custom-fields/{field} Update field
DELETE /api/v1/order-custom-fields/{field} Delete field

Create Custom Field:

{
  "field_key": "priority_level",
  "field_label": "Priority Level",
  "field_type": "select",
  "options": ["low", "normal", "high", "urgent"],
  "is_required": false,
  "is_filterable": true,
  "sort_order": 1
}

Custom field values are stored in the order's custom_fields JSONB column:

{
  "priority_level": "high",
  "gift_message": "Happy Birthday!"
}

Module API Bus Integration

The Orders module declares 10 bus endpoints and publishes 13 events. See Module API Bus for the bus architecture.

Provided Endpoints

Method Alias Description Mode
orders.create Create order via ingest pipeline sync
orders.createOrUpdate Idempotent upsert by source + external_id sync
orders.batchIngest Batch ingest multiple orders async
orders.get Get order by ID sync
orders.updateStatus Update order status sync
orders.getByExternalIds Find orders by source + external IDs sync
orders.getStats Get order statistics sync
orders.reserveInventory Publish inventory reservation event sync
orders.processReturn Process a return request sync
orders.processRefund Process a refund sync

Published Events

Event Trigger
order.created New order created via ingest pipeline
order.status_changed Order status updated
order.cancelled Order cancelled
order.fulfilled Order shipped or out for delivery
order.delivered Order delivered to customer
order.payment_updated Payment status changed
order.refund_processed Refund completed
order.return_requested Customer requested return
order.return_approved Return request approved
order.inventory_reserved Stock reserved for order
order.inventory_released Stock reservation released
order.shipment_created Shipment created with tracking
order.import_completed Batch import finished

Self-Subscriptions

The module subscribes to its own events for:

  • Inventory reservation: On order.created β†’ reserves stock
  • Inventory release: On order.cancelled β†’ releases reserved stock
  • Notifications: All order events β†’ checks notification rules, sends alerts
  • Outbound webhooks: All order events β†’ dispatches to registered webhook endpoints

Tax & Invoicing

GST Calculation

The TaxService handles Indian GST rules automatically:

  • Intra-state (same state): Split into CGST + SGST (half rate each)
  • Inter-state (different states): Full IGST at combined rate
$tax = app(TaxService::class);

// Intra-state: β‚Ή1000 @ 18% β†’ CGST β‚Ή90 + SGST β‚Ή90 = β‚Ή180
$result = $tax->calculateTax(1000, null, true, 18);

// Inter-state: β‚Ή1000 @ 18% β†’ IGST β‚Ή180
$result = $tax->calculateTax(1000, null, false, 18);

Tax rates can be configured per HSN code via TaxConfiguration model.

Invoice Generation

Invoices are auto-numbered with the format INV-YYYYMM-NNNN:

POST /api/v1/orders/{order}/invoices

// Response
{
  "invoice_number": "INV-202603-0001",
  "type": "invoice",
  "subtotal": 2500.00,
  "tax_details": {
    "cgst": 225,
    "sgst": 225,
    "rate": 18
  },
  "total_tax": 450.00,
  "total": 2950.00,
  "billing_name": "John Doe",
  "billing_gstin": "27AAPFU0939F1ZV"
}

Database Optimizations

The module includes PostgreSQL-specific optimizations for large-scale order volumes:

Optimization Purpose
JSONB columns GIN-indexable, supports @> containment queries
GIN index on tags Fast tag filtering
Composite indexes (status, created_at), (payment_status, created_at), (source_module, created_at)
Partial indexes WHERE status = 'pending', WHERE status = 'ready_to_ship' for hot queries
BRIN index on created_at Compact index for time-range scans on naturally ordered data

Queue Workers

The module uses dedicated queues for background processing:

Queue Jobs
default Notifications, webhooks, invoice PDF generation
imports CSV order imports (chunked, cancellable)
exports CSV/Excel order exports (cursor-based streaming)
module-bus Async bus calls (batch ingest)

Configure in Laravel Horizon for separate supervisor processes.

Order Metadata

Modules can attach arbitrary key-value metadata to orders:

// Set metadata
$order->setMeta('shopify_order_id', '12345', 'store-shopify');
$order->setMeta('delivery_notes', 'Ring doorbell twice', 'manual');

// Get metadata
$shopifyId = $order->getMeta('shopify_order_id');

Metadata is scoped by module_source to avoid conflicts between modules.

Navigation

Orders & Fulfillment
β”œβ”€β”€ All Orders
β”œβ”€β”€ Create Order
β”œβ”€β”€ Ready to Ship
β”œβ”€β”€ Shipped Orders
β”œβ”€β”€ Returns
β”œβ”€β”€ Analytics
β”œβ”€β”€ Import / Export
└── Settings (Notification Rules, Webhooks, Custom Fields)

Related Documentation

Other Modules