# Channel Order Sync

## Overview

Channel Order Sync ingests externally sourced demand into the internal sales workflow and tracks synchronization attempts with explicit operational records. A `ChannelOrder` stores the normalized representation of an order received from a marketplace, storefront, or distributor integration, while `SyncJob` records each import or export attempt, including status, timing, counts, and error context. `ChannelOrder` itself carries an operational lifecycle so users can distinguish demand that is waiting for conversion, already converted, blocked by an exception, or cancelled at the originating channel. This allows the business to treat channel-originated orders as first-class sales demand without losing traceability to the source payload and integration outcome.

The feature is intentionally scoped to reliable ingestion, normalization, deduplication, and operational retry handling. It does not embed storefront UX, checkout orchestration, or arbitrary channel API behavior in the order model. The main contract is that a successful sync should be able to create or update a `ChannelOrder`, map it to internal customers and listings, and then materialize or update a `SalesOrder` without bypassing the normal sales rules. The minimum `ChannelOrder` lifecycle is `PENDING_CONVERSION` → `CONVERTED`, with `EXCEPTION` for failed normalization or mapping and `CANCELLED_BY_CHANNEL` when the originating platform cancels the order before internal execution is complete.

## Business Purpose

- Automate order capture from external sales channels without manual re-entry
- Preserve source-channel traceability with normalized channel-order records and raw payload references
- Make synchronization reliable through explicit job records, retry semantics, and error visibility
- Prevent duplicate order creation when the same channel event arrives more than once
- Give operations teams explicit `ChannelOrder` states for pending conversion, converted demand, exception handling, and channel-originated cancellation queries
- Separate integration failure handling from internal sales-order lifecycle management
- Provide a governed path for pushing selected downstream status updates back to the originating channel

## Process Flow

```mermaid
flowchart TD
    A[Start SyncJob] --> B[Fetch or receive channel payloads]
    B --> C[Deduplicate by channel and external order identity]
    C --> D[Create or update ChannelOrder as PENDING_CONVERSION]
    D --> E[Resolve listing, customer, currency, and item mappings]
    E --> F{Mappings valid?}
    F -->|No| G[Move ChannelOrder to EXCEPTION and retain retry context]
    F -->|Yes| H[Create or update SalesOrder]
    H --> I[Move ChannelOrder to CONVERTED]
    I --> J[Record sync result on SyncJob]
    J --> K{Need outbound status sync later?}
    K -->|Yes| L[Queue later SyncJob for shipment or invoice status push]
    K -->|No| M[Complete SyncJob]
    G --> N[Retry after correction or transient recovery]
    D --> O{Cancellation received from channel?}
    O -->|Yes| P[Move ChannelOrder to CANCELLED_BY_CHANNEL]
```

## Scenario Patterns

- **Marketplace order import**: A marketplace sends a new order payload, which is normalized into `ChannelOrder` and converted into an internal sales order
- **Duplicate webhook delivery**: The same order event arrives twice, and the second attempt is treated idempotently without creating a duplicate sales order
- **Missing listing mapping**: A channel order references an unknown external listing identifier, so the job records an exception and waits for mapping correction
- **Transient channel failure**: A sync job fails because the external API times out; the job remains retryable with the original context preserved
- **Customer resolution fallback**: A channel order includes buyer identity that maps to an existing customer when possible and otherwise triggers a controlled customer-resolution step
- **Pending conversion queue**: A large batch import lands successfully, but several valid channel orders remain in `PENDING_CONVERSION` until the internal conversion worker is scheduled
- **Status push after shipment**: A later sync job publishes shipped status or tracking information back to the originating channel once the internal shipment posts
- **Cancellation update from channel**: A channel-originated cancellation request is received before fulfillment starts, the `ChannelOrder` moves to `CANCELLED_BY_CHANNEL`, and the internal sales order is updated through controlled sales cancellation rules when one already exists
- **Partial import batch**: A nightly sync imports ten orders successfully while two fail validation, and the job preserves both success and failure counts
- **Manual replay after fix**: Operations correct the missing listing or customer mapping and replay the failed sync without reprocessing the whole batch blindly

## Test Cases

- Starting a sync job should create a `SyncJob` record with channel, direction, start time, and status
- A channel order must be unique by company, channel, and external order identity
- A newly ingested channel order must enter `PENDING_CONVERSION` before any internal sales-order materialization occurs
- Receiving the same channel order event more than once should be idempotent and must not create duplicate internal sales orders
- A sync job must preserve raw payload reference or normalized source data sufficient for troubleshooting
- If a required listing mapping is missing, the sync should fail that record, move the `ChannelOrder` to `EXCEPTION`, and avoid creating a partial internal sales order
- If required customer, currency, or item resolution fails, the sync should keep actionable error context for retry and preserve the `EXCEPTION` state until replay succeeds
- A successful sync must create or update a `ChannelOrder` before creating or updating the internal `SalesOrder`
- Successful conversion must move the `ChannelOrder` to `CONVERTED` and store the linked internal `SalesOrder` reference
- Sync retries after transient failure should reuse the original channel order identity rather than create a new one
- Sync results must be isolated by company and must not create sales records in another company
- A sync job should record processed, succeeded, failed, and retried counts when applicable
- A channel cancellation event received before fulfillment starts must move the `ChannelOrder` to `CANCELLED_BY_CHANNEL`, and replaying stale conversion jobs afterward must be prevented by policy
- Outbound shipment or invoice status push should be limited to channel orders that preserve a valid originating channel reference
- A cancelled or inactive channel must not accept new sync jobs unless explicitly allowed for recovery or replay
- Only authorized integration or commerce users should be able to start, retry, cancel, or replay sync jobs
- Historical channel orders and sync jobs must remain queryable for audit and support even after the linked listing is retired
- A sync job should finish in a terminal status that distinguishes success, partial success, failure, and cancellation, independently of the `ChannelOrder` operational state

## Reference Links

- [Shopify order sync concepts](https://shopify.dev/docs/apps/build/orders-fulfillment/order-management-apps)
- [Amazon SP-API orders concepts](https://developer-docs.amazon.com/sp-api/)
- [Oracle omnichannel order capture documentation](https://docs.oracle.com/en/cloud/saas/supply-chain-and-manufacturing/index.html)
