
Inventory Tracking
- 153 installs
- 41 repo stars
- Updated March 13, 2026
- finsilabs/awesome-ecommerce-skills
Design and implement stock levels, reservations, low-stock alerts, and sync rules across storefront, warehouse, and fulfillment so overselling and stale counts are prevented.
About
Inventory-tracking from awesome-ecommerce-skills guides implementation of reliable stock management for online stores: quantity ledgers, reservation on checkout, replenishment signals, and cross-system sync so merchants avoid overselling and phantom availability.
- Models SKU quantities, reservations, and adjustments
- Defines low-stock thresholds and alert hooks
- Plans multi-location or channel sync strategies
- Prevents oversell with atomic decrement patterns
- Integrates warehouse, POS, and storefront updates
Inventory Tracking by the numbers
- 153 all-time installs (skills.sh)
- Ranked #2,479 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/finsilabs/awesome-ecommerce-skills --skill inventory-trackingAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 153 |
|---|---|
| repo stars | ★ 41 |
| Last updated | March 13, 2026 |
| Repository | finsilabs/awesome-ecommerce-skills ↗ |
What it does
Design and implement stock levels, reservations, low-stock alerts, and sync rules across storefront, warehouse, and fulfillment so overselling and stale counts are prevented.
Files
Inventory Tracking
Overview
Real-time inventory tracking prevents overselling by reserving stock when customers add items to their cart and decrementing it on order fulfillment. Every major e-commerce platform has this built in. Platform-native inventory tracking is almost always the right starting point — only build custom inventory logic if you have requirements that platforms cannot meet (complex multi-warehouse routing, custom reservation windows, or external WMS integration).
When to Use This Skill
- When overselling is occurring and orders are being placed for out-of-stock items
- When implementing multi-warehouse inventory with per-location stock levels
- When building backorder or pre-order functionality for out-of-stock products
- When a flash sale or product launch will create high-concurrency checkout attempts for limited-stock items
Core Instructions
Step 1: Determine platform and enable inventory tracking
| Platform | Built-in Inventory | Recommended Extension |
|---|---|---|
| Shopify | Native per-variant inventory tracking with location support | Stocky (free, by Shopify) for purchase orders and demand forecasting |
| WooCommerce | Native stock management with backorder support | ATUM Inventory Management for advanced multi-warehouse and supplier POs |
| BigCommerce | Native per-SKU inventory tracking with low-stock alerts | Multi-Location Inventory app for warehouse routing |
| Custom / Headless | Build atomic reservation with optimistic locking | Required for custom platforms without native inventory management |
---
Step 2: Platform-specific setup
---
Shopify
Shopify tracks inventory per variant, per location, natively.
Enable inventory tracking:
1. Go to Admin → Products → [Product] → [Variant] 2. Under Inventory, check Track quantity 3. Enter your quantity per location 4. For "Continue selling when out of stock" — only check this if you allow backorders for this product
Set up locations:
1. Go to Settings → Locations 2. Add each warehouse, store, or fulfillment center as a location 3. When editing a product variant, set the quantity at each location independently
Backorders:
- Enable per variant: check Continue selling when out of stock on the variant
- Or use a backorder app like Pre-Order Now or Back In Stock for more control (notify customers when available, collect pre-orders, etc.)
Oversell prevention during high traffic:
Shopify's checkout system holds inventory during the checkout process to prevent two customers from purchasing the last item simultaneously. For flash sales, use Shopify Scripts (Plus) or the Inventory Planner app to set purchase limits.
Inventory sync with physical locations:
- Install Stocky (free, by Shopify) for purchase orders and receiving
- Stocky automatically updates Shopify inventory when you receive a PO
- Use the Shopify POS app to sync inventory between your online store and physical retail locations
---
WooCommerce
WooCommerce has built-in stock management.
Enable inventory tracking:
1. Go to WooCommerce → Settings → Products → Inventory 2. Check Enable stock management 3. Set Hold stock (minutes) — this is the inventory reservation window during checkout (default: 60 minutes)
Per-product settings:
1. Go to WooCommerce → Products → [Product] → Inventory tab 2. Enable Manage stock? 3. Enter Stock quantity 4. Set Backorders: "Do not allow" / "Allow, but notify customer" / "Allow" 5. Set a Low stock threshold for this product
Advanced inventory management with ATUM:
1. Install ATUM Inventory Management for WooCommerce (free core, paid advanced features) 2. ATUM provides a central inventory dashboard showing stock levels across all products 3. Add purchase orders through ATUM → Purchase Orders → Add PO 4. Receiving a PO in ATUM automatically increments WooCommerce stock 5. For multi-warehouse: ATUM's Multi-Inventory add-on ($) assigns stock per location and routes fulfillment
---
BigCommerce
BigCommerce tracks inventory per SKU natively.
Enable inventory tracking:
1. Go to Products → [Product] → Inventory tab 2. Set Inventory tracking to "By product" or "By option" (for variants) 3. Enter the current stock level 4. Set a Low stock level for alerts
Multi-location inventory:
- Install the Multi-Location Inventory app from the BigCommerce App Marketplace
- Assign stock quantities per location
- Set fulfillment routing rules (ship from closest, ship from cheapest, etc.)
Backorders:
- BigCommerce handles this natively — set Allow backorders on products you want to continue selling when out of stock
- The product page shows "Ships in X days" when on backorder
---
Custom / Headless
For custom platforms, implement atomic inventory reservation using optimistic concurrency control to prevent overselling under high load:
// lib/inventory.ts
const MAX_RETRIES = 3;
// Reserve inventory atomically — handles concurrent requests safely
export async function reserveInventory({
variantId, locationId, quantity, referenceId
}: { variantId: string; locationId: string; quantity: number; referenceId: string }) {
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
const level = await db.inventoryLevels.findUnique({
where: { variantId_locationId: { variantId, locationId } },
});
if (!level) throw new Error(`Inventory not found: ${variantId}`);
const available = level.onHand - level.reserved;
if (available < quantity && !level.backorderAllowed) {
throw new Error(`Insufficient stock: ${available} available, ${quantity} requested`);
}
// Optimistic update — only succeeds if version hasn't changed (no concurrent modifications)
const updated = await db.inventoryLevels.updateMany({
where: { variantId_locationId: { variantId, locationId }, version: level.version },
data: { reserved: level.reserved + quantity, version: level.version + 1 },
});
if (updated.count === 0) {
// Another process modified inventory concurrently; retry
await new Promise(r => setTimeout(r, 50 * (attempt + 1)));
continue;
}
// Log the transaction for audit trail
await db.inventoryTransactions.create({
data: { variantId, locationId, type: 'reserve', quantity: -quantity, referenceId },
});
return { success: true, remaining: available - quantity };
}
throw new Error(`Failed to reserve inventory after ${MAX_RETRIES} retries`);
}
// Release reservation when cart expires or order is cancelled
export async function releaseReservation({
variantId, locationId, quantity, referenceId
}: { variantId: string; locationId: string; quantity: number; referenceId: string }) {
// Idempotency check — don't release twice
const existing = await db.inventoryTransactions.findFirst({
where: { type: 'release', referenceId, variantId },
});
if (existing) return;
await db.$transaction([
db.inventoryLevels.update({
where: { variantId_locationId: { variantId, locationId } },
data: { reserved: { decrement: quantity } },
}),
db.inventoryTransactions.create({
data: { variantId, locationId, type: 'release', quantity: +quantity, referenceId },
}),
]);
}
// Expire stale cart reservations — run every 5-10 minutes via cron
export async function expireStaleCartReservations() {
const TTL_MINUTES = 30;
const cutoff = new Date(Date.now() - TTL_MINUTES * 60 * 1000);
const staleCarts = await db.carts.findMany({
where: { status: 'active', updatedAt: { lt: cutoff }, reservedAt: { not: null } },
include: { items: true },
});
for (const cart of staleCarts) {
for (const item of cart.items) {
await releaseReservation({ variantId: item.variantId, locationId: item.locationId, quantity: item.quantity, referenceId: cart.id });
}
}
}---
Step 3: Configure backorder behavior
Backorders allow customers to purchase even when stock is at zero, with a clear expectation of a delayed delivery.
When to allow backorders:
- Products with reliable supplier lead times (7–14 days)
- Made-to-order products
- Pre-order campaigns for upcoming products
When to block backorders:
- Products with unreliable supply
- Third-party fulfilled items where you don't control restock
Communication best practices:
- Show "Ships in 7–10 days" on the product page when stock is 0 and backorders are enabled
- Include expected ship date in the order confirmation email
- Notify customers proactively if the expected date changes
---
Step 4: Set up inventory alerts
Pair inventory tracking with low-stock alerts — see the @low-stock-alerts skill for full setup. Quick summary:
- Shopify: Go to Admin → Products — Shopify shows a low stock indicator; use Stocky for email alerts
- WooCommerce: Go to WooCommerce → Settings → Products → Inventory → Low stock threshold — WooCommerce emails the store admin when stock crosses this threshold
- BigCommerce: Go to Products → [Product] → Inventory → Low stock level — BigCommerce sends email notifications automatically
Best Practices
- Enable inventory tracking on every SKU — products without tracking can be oversold silently; only disable tracking for digital products or items with unlimited supply
- Set a reservation window for in-progress checkouts — WooCommerce's "Hold stock" setting (default 60 min) prevents inventory from being held indefinitely by abandoned carts
- Test your oversell protection — during a flash sale setup, manually verify that the last unit cannot be purchased twice by simulating two simultaneous checkouts
- Log every inventory change — platforms log this natively; for custom builds, an immutable
inventory_transactionstable is essential for diagnosing discrepancies - Use the platform's built-in multi-location inventory before buying a third-party app — Shopify, WooCommerce, and BigCommerce all handle multiple locations natively
Common Pitfalls
| Problem | Solution |
|---|---|
| Overselling during flash sales on Shopify | Shopify's checkout holds inventory during the checkout flow; for very high-concurrency launches, set purchase limits using Shopify Scripts (Plus) or use a waitlist app |
| WooCommerce inventory not decremented after order | Check that stock management is enabled on the product AND globally in WooCommerce settings; both must be on |
| Inventory released immediately when order is cancelled before fulfillment | This is correct behavior for physical goods — released inventory becomes available for other customers; only delay release for backordered items |
| Negative inventory after manual adjustment | Add validation in WooCommerce (ATUM) or set a DB check constraint on custom builds; reserved >= 0 and on_hand >= 0 |
| Shopify location not receiving inventory updates from POS | Ensure POS is connected to the correct Shopify location in Settings → Locations → POS channel |
Related Skills
- @multi-warehouse
- @low-stock-alerts
- @variant-matrix
{
"context": "Tests whether the agent implements inventory reservation using optimistic concurrency control (version-based locking) with correct retry logic, proper error types, and transaction logging — rather than naive read-then-write or serialized locking approaches.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Optimistic lock in UPDATE",
"max_score": 15,
"description": "The conditional UPDATE (or equivalent) includes a filter on the current version value — i.e. the WHERE clause checks `version = <current version>` so that the update only succeeds if no other writer changed it first"
},
{
"name": "No SELECT FOR UPDATE",
"max_score": 10,
"description": "The code does NOT use SELECT FOR UPDATE (or any equivalent pessimistic locking hint like FOR SHARE, LOCK IN SHARE MODE) anywhere in the reservation path"
},
{
"name": "Version increment on write",
"max_score": 10,
"description": "On a successful reservation the version field is incremented by 1 in the same update that modifies reserved quantity"
},
{
"name": "MAX_RETRIES equals 3",
"max_score": 10,
"description": "The retry loop runs at most 3 attempts (MAX_RETRIES = 3 or equivalent constant set to 3)"
},
{
"name": "Retry delay formula",
"max_score": 10,
"description": "The delay between retries is 50 * (attempt + 1) milliseconds (i.e. 50ms, 100ms, 150ms) — not a fixed delay, not exponential doubling"
},
{
"name": "Typed insufficient-stock error",
"max_score": 10,
"description": "When available stock is less than the requested quantity and backorder is not enabled, the code throws a specific InventoryInsufficientError (or analogous named class/type) rather than a plain Error or generic exception"
},
{
"name": "Backorder bypass check",
"max_score": 10,
"description": "The insufficient-stock check is guarded by backorderAllowed: when backorder is allowed the function does NOT throw the insufficient error and proceeds with the reservation"
},
{
"name": "Transaction log on success",
"max_score": 10,
"description": "After a successful optimistic update, a record is written to the inventory_transactions (or equivalent audit) table with type='reserve'"
},
{
"name": "Transaction quantity is negative",
"max_score": 10,
"description": "The transaction log entry for a reservation stores quantity as a negative number (−quantity), representing a decrease in available stock"
},
{
"name": "Error after retries exhausted",
"max_score": 5,
"description": "After all retry attempts are exhausted without a successful update, the function throws an error (not silently returns)"
}
]
}
Limited-Edition Drop: Concurrent Checkout Protection
Problem/Feature Description
A streetwear brand is launching a limited-edition sneaker collection on their e-commerce platform. Previous drops suffered from significant overselling — customers completed checkout for sizes that were already gone, leading to order cancellations, refund headaches, and social media backlash. The engineering team has traced the root cause: the current system reads available stock, decides whether to proceed, and then decrements in two separate database queries. Under normal load this works fine, but during a product drop with thousands of simultaneous checkout attempts the gap between read and write is long enough for dozens of users to see the same "in stock" result before any reservation is committed.
The team needs a reserveInventory function that safely handles concurrent requests for the same variant-location combination. The function must cope with situations where a reservation attempt collides with another in-flight write, recover gracefully, and ultimately either succeed or report exactly why it could not fulfill the request. It should also support backorder scenarios for certain variants where the brand will accept orders beyond physical stock up to a supplier-confirmed limit.
Output Specification
Write the implementation as a JavaScript/TypeScript module at lib/inventory.js (or .ts). The file should export a reserveInventory function. You may define any helper classes or types needed (e.g., a custom error class).
Also produce a lib/inventory.test.js (or .test.ts) file with at least 3 unit/integration test cases that demonstrate:
- A successful reservation
- A failed reservation due to insufficient stock
- The retry behaviour when a concurrent write is detected
You may use any test framework (Jest, Vitest, node:test, etc.) and mock the database layer as needed. The tests do not need to pass against a live database — stubs/mocks are fine.
{
"context": "Tests whether the agent correctly implements reservation release with idempotency protection, uses atomic database transactions for multi-step writes, and builds a background job that uses the correct TTL value and staleness field when expiring abandoned cart reservations.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Idempotency check before release",
"max_score": 15,
"description": "releaseReservation (or equivalent) queries the transaction log for an existing 'release' record with matching referenceId and variantId before performing the release — and skips execution if one is found"
},
{
"name": "Warn on duplicate release",
"max_score": 8,
"description": "When a duplicate release is detected the code issues a warning (console.warn or equivalent log) rather than throwing an error"
},
{
"name": "Atomic release transaction",
"max_score": 15,
"description": "The release operation updates reserved inventory AND writes the transaction log entry inside a single database transaction (db.$transaction or equivalent) — not two sequential independent writes"
},
{
"name": "Release log type and quantity",
"max_score": 10,
"description": "The transaction log entry written during release has type='release' and quantity is a POSITIVE number (representing the units returned to available stock)"
},
{
"name": "TTL of 30 minutes",
"max_score": 10,
"description": "The cart expiry job uses a reservation TTL of exactly 30 minutes (RESERVATION_TTL_MINUTES = 30 or equivalent constant)"
},
{
"name": "Staleness based on updatedAt",
"max_score": 15,
"description": "The background job determines cart staleness by comparing cart.updatedAt (NOT cart.createdAt) to the cutoff timestamp"
},
{
"name": "reservedAt cleared after expiry",
"max_score": 10,
"description": "After releasing all items for an expired cart, the cart record is updated to set reservedAt to null"
},
{
"name": "Job runs every 5-10 minutes",
"max_score": 7,
"description": "The background job is scheduled or documented to run on an interval between 5 and 10 minutes (e.g. cron expression, setInterval, or inline comment/config value reflecting this cadence)"
},
{
"name": "Only active carts processed",
"max_score": 10,
"description": "The expiry job filters carts by status='active' and reservedAt IS NOT NULL — ignoring already-released or checked-out carts"
}
]
}
Abandoned Cart Cleanup and Safe Order Cancellation
Problem/Feature Description
A home-goods marketplace has been seeing a growing problem: customers browse during peak hours, add items to their cart, then leave without checking out. Because the platform optimistically reserves inventory when items are added to the cart, a significant portion of available stock is being held hostage by abandoned sessions. During a recent weekend sale, roughly 18% of reserved units belonged to carts that had not been touched in over an hour — units that could have been sold to other customers.
The team also discovered a related bug: when an order was cancelled after a payment failure, a race condition in the cancellation webhook occasionally triggered the release call twice. The second call was then decrementing the reserved counter below zero, causing subtle inventory corruption that only showed up in end-of-day reconciliation reports.
The team needs two things: (1) a releaseReservation function that is safe to call multiple times for the same cart/order without corrupting inventory, and (2) a background job that periodically reclaims inventory from carts that customers have clearly abandoned.
Output Specification
Write the implementation as a JavaScript/TypeScript module at lib/inventory.js (or .ts). Export:
releaseReservation({ variantId, locationId, quantity, referenceId })— releases inventory held by a cart or orderexpireStaleCartReservations()— scans for abandoned carts and releases their inventory
Also write a jobs/expireCartReservations.js (or equivalent) file that imports and schedules expireStaleCartReservations to run on a recurring basis.
Produce a short DESIGN.md explaining the approach taken to make release safe against duplicate calls and how the expiry cutoff is computed.
{
"context": "Tests whether the agent designs the correct inventory data model (including version, backorder_limit, reorder_point, and the transactions audit table), implements fulfillment correctly, aggregates multi-warehouse availability properly, and includes schema-level guards against data corruption.",
"type": "weighted_checklist",
"checklist": [
{
"name": "version column present",
"max_score": 8,
"description": "The inventory_levels table definition includes a 'version' column (integer/bigint) used as an optimistic lock counter"
},
{
"name": "backorder_limit column present",
"max_score": 8,
"description": "The inventory_levels table includes a 'backorder_limit' column (with 0 meaning unlimited or a sensible default)"
},
{
"name": "reorder_point column present",
"max_score": 8,
"description": "The inventory_levels table includes a 'reorder_point' column per variant-location"
},
{
"name": "Transactions table type enum",
"max_score": 8,
"description": "The inventory_transactions table (or equivalent audit log) defines a 'type' field restricted to the values: reserve, release, fulfill, receive, adjust"
},
{
"name": "Transactions quantity sign convention",
"max_score": 7,
"description": "The inventory_transactions table documentation or code comments explicitly states that negative quantity = decrease and positive quantity = increase (or the implementation consistently applies this convention)"
},
{
"name": "available derived from on_hand minus reserved",
"max_score": 10,
"description": "available is either stored as on_hand - reserved or computed on read as on_hand - reserved — it is NOT stored as an independent value that diverges from these two fields"
},
{
"name": "CHECK constraint reserved >= 0",
"max_score": 10,
"description": "The schema includes a database-level CHECK constraint (or equivalent) ensuring reserved cannot go below 0"
},
{
"name": "fulfillInventory decrements both fields",
"max_score": 12,
"description": "The fulfillInventory function (or equivalent) decrements BOTH on_hand AND reserved in the same atomic operation — not only on_hand"
},
{
"name": "Multi-warehouse uses Math.max(0, available)",
"max_score": 12,
"description": "The multi-warehouse availability aggregation clamps each location's contribution to Math.max(0, available) before summing — preventing locations with negative available from reducing the total"
},
{
"name": "Multi-warehouse returns byLocation",
"max_score": 10,
"description": "The availability function returns a per-location breakdown (byLocation map or equivalent) in addition to the total"
},
{
"name": "backorder_limit enforced in reservation",
"max_score": 7,
"description": "The reserveInventory function (or equivalent) checks backorder_limit when backorder is allowed and rejects reservations that would exceed this limit (when backorder_limit > 0)"
}
]
}
Multi-Warehouse Inventory System: Data Layer
Problem/Feature Description
A fashion retailer is expanding from a single fulfilment centre to a four-warehouse network spanning different regions. Their current inventory system was built for a single location: a flat table of SKU → quantity. It has no concept of per-warehouse stock, no way to accept backorders when stock runs out (even though the buying team pre-orders from suppliers weeks in advance), and no mechanism to alert the warehouse team when replenishment is needed.
As part of this expansion the engineering team needs to design and implement the core data layer from scratch. The data model must support per-location stock levels with enough metadata to drive backorder acceptance, replenishment workflows, and concurrent-safe writes. Importantly, the team has been burned by inventory corruption in the past — manual stock adjustments sometimes leave counts in impossible states (negative reserved quantities) — so the new schema must make these corruptions impossible at the database level.
A key operational requirement is a consolidated availability endpoint: when a customer views a product page the frontend must show total available stock, but the fulfilment team also needs a per-warehouse breakdown to decide which warehouse ships each order.
Output Specification
Produce the following files:
schema.sqlorprisma/schema.prisma(or equivalent ORM schema file) — the full table/model definitions for the inventory data layerlib/inventory.js(or.ts) — exporting at minimum:fulfillInventory({ variantId, locationId, quantity, orderId })— called when an order shipsgetTotalAvailability(variantId, fulfillableLocationIds)— returns aggregated and per-location availabilityreserveInventory({ variantId, locationId, quantity, referenceId })— reserves stock with backorder supportDESIGN.md— a brief document explaining the schema decisions, how available stock is computed, and how the system prevents impossible inventory states
Do not download or install a real database — use pseudo-SQL, Prisma schema syntax, or plain JavaScript objects/comments to represent the schema. The focus is on the correctness of the design and implementation logic, not on running a live database.
{
"name": "finsi/inventory-tracking",
"version": "0.1.0",
"summary": "Real-time stock tracking across warehouses with reservation and backorder logic",
"skills": {
"inventory-tracking": {
"path": "SKILL.md"
}
}
}