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.1.tar.gz (23.1 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.1-py3-none-any.whl (21.0 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: inventra-1.0.1.tar.gz
  • Upload date:
  • Size: 23.1 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.1.tar.gz
Algorithm Hash digest
SHA256 362b4ed428bde34c1c14af673cb4acec63b0a962dfc7a198788da2a834c4de31
MD5 396deae49a0f8d07f5a9840a9389fa18
BLAKE2b-256 a66b266677f2edcc13ff31640d2ffb7fe56277f0b89b8d5e4db1c009c5a66cd8

See more details on using hashes here.

File details

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

File metadata

  • Download URL: inventra-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 21.0 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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 92cd00fdd756d09bd5b1dcc65417470c226cbc50ab843caf5b23fa1f8f05f9c7
MD5 ba2f084e3f56be5448dfd1cb4a0a5ca9
BLAKE2b-256 1eab41b419990bc8762d9ad96f8b6badb3d6cf6e3c8161f6e12f1fdf099e0f4c

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