Skip to content

aravikishan/ShopifyOdooSync

Repository files navigation

Shopify-Odoo Sync Simulator

A production-grade simulation of bidirectional data synchronization between a Shopify storefront and an Odoo ERP system. Built with FastAPI, SQLite, and a vanilla-JS SPA frontend.

No external APIs required. Both Shopify and Odoo are fully mocked in-process with realistic data stores.


Table of Contents


Features

  1. Mock Shopify Store -- 20 products with variants (S/M/L/XL), inventory levels per variant, and 50 realistic orders with line items.
  2. Mock Odoo ERP -- Product templates, stock quants, and sale orders mirroring Odoo's XML-RPC external API patterns.
  3. Product Sync -- Map Shopify fields to Odoo fields (title -> name, price -> list_price, SKU -> default_code, etc.) with configurable transforms (direct, uppercase, lowercase, round2).
  4. Inventory Sync -- Synchronize stock levels between systems with conflict detection and resolution strategies.
  5. Order Sync -- Push Shopify orders to Odoo sale orders with line item mapping.
  6. Configurable Field Mappings -- Full CRUD for mapping rules. Choose which Shopify field maps to which Odoo field and apply transforms.
  7. Retry with Exponential Back-off -- 1s, 2s, 4s, 8s delays on simulated transient failures. Configurable max retries and base delay.
  8. Dead Letter Queue (DLQ) -- Records that fail after max retries are stored with full payload and error details. Manual retry available.
  9. Sync Job History -- Every sync run creates a job with per-record logs (success / fail / skipped).
  10. Dashboard -- Last sync time, records synced, failure rate, DLQ count, and system-pair inventory counts.
  11. Conflict Resolution -- Two strategies: last-write-wins (default) and source-priority (configurable which system wins).
  12. Dry Run Mode -- Preview what would sync without writing data.

Architecture

+--------------------+          +-------------------+
|   Shopify Mock     |  <--->   |   Sync Engine     |  <--->  |   Odoo Mock     |
|   (in-memory)      |          | - field mapping   |         |   (in-memory)   |
| - products/variants|          | - conflict resolve|         | - product.templ |
| - inventory levels |          | - retry + backoff |         | - stock.quant   |
| - orders           |          | - DLQ management  |         | - sale.order    |
+--------------------+          +-------------------+         +-----------------+
                                        |
                                   SQLite DB
                               (jobs, logs, DLQ,
                                field mappings)

Quick Start

# Clone
git clone <repo-url>
cd shopify-odoo-sync

# Option A: use start.sh
chmod +x start.sh
./start.sh

# Option B: manual
python -m venv venv
source venv/bin/activate        # Windows: venv\Scripts\activate
pip install -r requirements.txt
mkdir -p instance
uvicorn app:app --host 0.0.0.0 --port 8033 --reload

Open http://localhost:8033 in your browser.

First-time setup

  1. Click Seed Mock Data on the dashboard (or POST /api/seed).
  2. Trigger individual syncs or use Sync All.
  3. View job history, logs, and dead-letter queue via the SPA.

Docker

# Build and run
docker compose up --build -d

# View logs
docker compose logs -f

# Stop
docker compose down

API Reference

Method Path Description
POST /api/seed Populate mock Shopify & Odoo data
POST /api/sync/products Sync products (Shopify -> Odoo)
POST /api/sync/inventory Sync inventory levels
POST /api/sync/orders Sync orders (Shopify -> Odoo)
GET /api/jobs List sync jobs
GET /api/jobs/{id} Get job detail
GET /api/jobs/{id}/logs Get per-record logs for a job
GET /api/mappings List all field mappings
POST /api/mappings Create a field mapping
PUT /api/mappings/{id} Update a field mapping
DELETE /api/mappings/{id} Delete a field mapping
GET /api/dead-letter List dead-letter entries
POST /api/dead-letter/{id}/retry Retry a failed DLQ entry
GET /api/health Health check
GET /api/dashboard Dashboard statistics

Example: Sync Products

# Seed first
curl -X POST http://localhost:8033/api/seed

# Run product sync
curl -X POST http://localhost:8033/api/sync/products \
  -H "Content-Type: application/json" \
  -d '{"direction": "shopify_to_odoo", "dry_run": false}'

Response:

{
  "id": 1,
  "job_type": "products",
  "status": "completed",
  "total_records": 20,
  "success_count": 18,
  "fail_count": 2,
  "skip_count": 0
}

Example: Field Mapping

# Create custom mapping
curl -X POST http://localhost:8033/api/mappings \
  -H "Content-Type: application/json" \
  -d '{"record_type": "product", "shopify_field": "handle", "odoo_field": "website_slug", "transform": "lowercase"}'

Configuration

Variable Default Description
SYNC_PORT 8033 Server port
SYNC_DEBUG false Enable debug mode
DATABASE_URL sqlite Database connection string
SYNC_FAILURE_RATE 0.10 Simulated failure rate (0.0-1.0)
MAX_RETRIES 4 Maximum retry attempts
RETRY_BACKOFF_BASE 1.0 Base retry delay (seconds)
RETRY_BACKOFF_MULTIPLIER 2.0 Backoff multiplier
DLQ_MAX_SIZE 5000 Maximum dead-letter queue size
SYNC_BATCH_SIZE 50 Records per sync batch
CONFLICT_STRATEGY last_write_wins Conflict resolution strategy
PRIORITY_SOURCE shopify Priority source (source_priority)

Frontend Views

Route Description
#/ Dashboard: sync status, counts, quick actions
#/sync Sync Control: trigger product/inventory/order sync
#/jobs Job History: list jobs with clickable log detail
#/mappings Field Mappings: CRUD editor with tabs per type
#/dead-letter Dead Letter Queue: failed records with retry

Testing

# Run all tests
pytest tests/ -v

# Run with coverage
pytest tests/ -v --cov=. --cov-report=term-missing

# Run specific test file
pytest tests/test_api.py -v
pytest tests/test_services.py -v
pytest tests/test_models.py -v
pytest tests/test_sync_engine.py -v

Sync Engine Details

Field Mapping

Each mapping defines:

  • record_type: product, inventory, or order
  • shopify_field: dot-notation path (e.g., variants.0.price)
  • odoo_field: target field name (e.g., list_price)
  • transform: direct, uppercase, lowercase, round2, to_float, to_int, to_str

Conflict Resolution

Two strategies are supported:

  1. last_write_wins (default): The most recent write takes precedence. During sync, the source system's data always overwrites the target.

  2. source_priority: A designated system always wins. Set PRIORITY_SOURCE=shopify or PRIORITY_SOURCE=odoo. When the priority source is the target, incoming updates are skipped.

Retry Logic

Attempt 1: immediate
Attempt 2: wait 1.0s  (base * 2^0)
Attempt 3: wait 2.0s  (base * 2^1)
Attempt 4: wait 4.0s  (base * 2^2)
Attempt 5: wait 8.0s  (base * 2^3)
-> Dead Letter Queue

Configurable via MAX_RETRIES, RETRY_BACKOFF_BASE, RETRY_BACKOFF_MULTIPLIER.

Dead Letter Queue

Records that exhaust all retries are placed in the DLQ with:

  • Full original payload (JSON)
  • Error message from last attempt
  • Retry count and max retries
  • Manual retry endpoint: POST /api/dead-letter/{id}/retry

Project Structure

shopify-odoo-sync/
├── app.py                          # FastAPI entry point (no app.run)
├── config.py                       # Environment-based configuration
├── requirements.txt                # Python dependencies
├── README.md                       # This file
├── Dockerfile                      # Container image
├── docker-compose.yml              # Docker Compose stack
├── start.sh                        # Quick-start script
├── LICENSE                         # MIT License
├── .gitignore                      # Git ignore rules
├── .github/
│   └── workflows/
│       └── ci.yml                  # GitHub Actions CI pipeline
├── models/
│   ├── __init__.py                 # Package exports
│   ├── database.py                 # SQLAlchemy engine, tables, init_db
│   └── schemas.py                  # Pydantic schemas
├── routes/
│   ├── __init__.py                 # Package exports
│   ├── api.py                      # REST API endpoints
│   └── views.py                    # HTML view (SPA shell)
├── services/
│   ├── __init__.py                 # Package exports
│   ├── mock_shopify.py             # Simulated Shopify Admin API
│   ├── mock_odoo.py                # Simulated Odoo XML-RPC API
│   └── sync_engine.py              # Bidirectional sync logic
├── frontend/
│   ├── index.html                  # SPA shell
│   └── static/
│       ├── css/
│       │   └── app.css             # Application styles (300+ lines)
│       └── js/
│           └── app.js              # SPA JavaScript (500+ lines)
├── tests/
│   ├── conftest.py                 # Shared fixtures
│   ├── test_api.py                 # API route tests
│   ├── test_models.py              # Schema tests
│   ├── test_services.py            # Mock service tests
│   └── test_sync_engine.py         # Sync engine integration tests
└── seed_data/
    └── data.json                   # Initial mock data

Development

Adding a New Sync Type

  1. Add mock data to services/mock_shopify.py and services/mock_odoo.py
  2. Create field mappings in models/database.py _seed_default_mappings()
  3. Add sync function in services/sync_engine.py
  4. Register API endpoint in routes/api.py
  5. Add frontend tab in frontend/static/js/app.js

Adjusting Failure Simulation

Set SYNC_FAILURE_RATE to control how often sync operations fail:

  • 0.0 -- 100% success (good for demos)
  • 0.10 -- 10% failure rate (default, realistic)
  • 0.50 -- 50% failure rate (stress-test DLQ)
  • 1.0 -- 100% failure (all records go to DLQ)

Custom Transforms

Add new transforms to _TRANSFORMS in services/sync_engine.py:

_TRANSFORMS["my_transform"] = lambda v: custom_logic(v)

Then reference it in field mappings via the API or database.


Resume Keywords

E-commerce integration, ERP synchronization, Shopify API, Odoo XML-RPC, bidirectional sync, field mapping engine, conflict resolution, last-write-wins, source-priority, exponential backoff, retry logic, dead letter queue, data pipeline, ETL, FastAPI, SQLAlchemy, SQLite, REST API, SPA dashboard, job scheduling, per-record logging, configurable transforms, dry-run mode, Docker, CI/CD, GitHub Actions, production-grade simulation.


License

MIT License. See LICENSE for details.

About

Shopify-Odoo bidirectional sync simulator with product/inventory/order sync, field mapping, retry logic, and dead letter queue

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors