Skip to main content

Multi-channel inventory sync for small and mid-market eCommerce — real-time conflict resolution, async sync, fraud-safe audit log

Project description

inventra

Multi-channel inventory sync for small and mid-market eCommerce — real-time conflict resolution, async sync engine, drift detection, and audit log.

Sync inventory across Shopify, Amazon, eBay, WooCommerce, and custom channels with automatic conflict resolution. No $500/mo SaaS required.

PyPI version Python 3.8+

The Problem

$1 trillion/year is lost to stockouts. 34% of small merchants report real-time sync as their top operational headache. Enterprise tools start at $500/mo — nothing affordable exists for independent sellers.

Installation

pip install inventra

Quick Start

from inventra import InventorySync, InventoryItem, ConflictResolution
from inventra.channels.mock import MockChannelAdapter

# Set up two channels with different stock levels
shopify = MockChannelAdapter(config={}, initial_inventory={"SKU-001": 50, "SKU-002": 10})
shopify.channel_name = "shopify"
shopify.connect()

amazon = MockChannelAdapter(config={}, initial_inventory={"SKU-001": 45, "SKU-002": 10})
amazon.channel_name = "amazon"
amazon.connect()

# Sync with lowest-stock wins (prevents overselling)
sync = InventorySync(
    channels=[shopify, amazon],
    conflict_resolution=ConflictResolution.LOWEST_STOCK,
)

report = sync.sync()
print(report.summary())
# {'total': 2, 'success': 2, 'failed': 0, 'conflicts': 1, ...}

Async Sync

import asyncio
from inventra import InventorySync

sync = InventorySync(channels=[shopify, amazon])

async def main():
    report = await sync.async_sync()
    print(report.summary())

asyncio.run(main())

Conflict Resolution Strategies

Strategy Behaviour
LOWEST_STOCK Sync to the lowest quantity — prevents overselling (default)
HIGHEST_STOCK Sync to the highest quantity
LATEST_UPDATE Use the most recently updated channel
CHANNEL_PRIORITY Respect a user-defined channel priority order
MANUAL Raise ConflictError — let your code decide
sync = InventorySync(conflict_resolution=ConflictResolution.CHANNEL_PRIORITY)
sync.set_channel_priority(["shopify", "amazon", "ebay"])

Building a Custom Channel Adapter

from inventra.channels.base import BaseChannelAdapter
from inventra.models import InventoryItem, SyncEvent, SyncStatus

class MyShopifyAdapter(BaseChannelAdapter):
    channel_name = "shopify"

    def connect(self):
        # authenticate with Shopify API
        self._connected = True

    def disconnect(self):
        self._connected = False

    def get_inventory(self, skus=None):
        # fetch from Shopify Inventory API
        ...

    def update_inventory(self, item):
        # push update to Shopify
        return SyncEvent(sku=item.sku, channel=self.channel_name, status=SyncStatus.SUCCESS, new_quantity=item.quantity)

Advanced Features

Pipeline

from inventra import SyncPipeline

pipeline = (
    SyncPipeline()
    .filter(lambda item: item.quantity > 0, name="exclude_oos")
    .map(lambda items: [i.model_copy(update={"reserved": 2}) for i in items], name="reserve_safety_stock")
    .with_retry(count=2)
)

result = pipeline.run(items)
print(pipeline.audit_log())

Caching

from inventra import InventoryCache

cache = InventoryCache(max_size=500, ttl_seconds=120)

@cache.memoize
def expensive_fetch(channel_name):
    ...

print(cache.stats())
# {'hits': 10, 'misses': 2, 'hit_rate': 0.833, 'size': 1}

Diff & Change Detection

from inventra import diff_inventory

diff = diff_inventory(snapshot_before, snapshot_after)
print(diff.summary())  # {'added': 2, 'removed': 0, 'modified': 5}
print(diff.to_json())

Async Batch

from inventra import abatch_sync_items, CancellationToken

token = CancellationToken()
events = await abatch_sync_items(items, adapter.update_inventory, max_concurrency=8, token=token)

Rate Limiter

from inventra import RateLimiter

limiter = RateLimiter(rate=10, capacity=10)  # 10 ops/sec
if limiter.acquire():
    adapter.update_inventory(item)

Streaming

from inventra import inventory_to_ndjson

for line in inventory_to_ndjson(items):
    socket.send(line)

Audit Log + PII Scrubbing

from inventra import AuditLog, PIIScrubber

log = AuditLog()
log.record("update", sku="SKU-001", channel="shopify", detail="qty 50→45")

metadata = {"note": "Customer email: user@example.com"}
clean = PIIScrubber.scrub_metadata(metadata)
# {'note': 'Customer email: [EMAIL]'}

Supported Channels

inventra ships with a MockChannelAdapter for testing. Community and official adapters (Shopify, Amazon, eBay, WooCommerce, Etsy) can be built by subclassing BaseChannelAdapter.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

inventra-1.0.0.tar.gz (16.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

inventra-1.0.0-py3-none-any.whl (14.4 kB view details)

Uploaded Python 3

File details

Details for the file inventra-1.0.0.tar.gz.

File metadata

  • Download URL: inventra-1.0.0.tar.gz
  • Upload date:
  • Size: 16.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for inventra-1.0.0.tar.gz
Algorithm Hash digest
SHA256 3a083f991848b47c4fae0fb226886aaffe99e5f03d1dade3afdc9bd0b795c0e0
MD5 87eaeac78c7fc6cebd4b08cc0bfea1d7
BLAKE2b-256 b42c5c44d26f1a0cd338d2bda065238fb18ef427d9a2e17259ff1d1f0eed341a

See more details on using hashes here.

File details

Details for the file inventra-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: inventra-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 14.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for inventra-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0862ed7b8b300aabac6baa0ac61cfffe7e49d7f6e0279dc83bd39a89e710256f
MD5 754cbd0d91df0f512fbdc35a21edf900
BLAKE2b-256 2e0e03a3357d96f15b0eff12a2a7fa2d58238cadf50df4b66e5e785071eb0bbd

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page