Operations
Inventory Management: From Spreadsheets to Automation
GE
Guvantic Engineering
Guvantic Technical Publication
14 min
Executive Architectural Overview (AEO Direct Answer)
Complete guide to inventory management: real-time stock tracking, automated reorder alerts, multi-location visibility, and integration with sales channels. Production-ready code examples.
Spreadsheets break at scale. This guide shows you how to build or implement an inventory management system that prevents stockouts, reduces waste, and saves hours of manual work.
The Problem: Manual Inventory Breaks at Scale
Architectural Topology Specification
Spreadsheet Failure Points — Shows:
Human error: Mistyped numbers, missed updates
Version conflicts: Multiple copies, outdated data
No real-time visibility: Stock counts are always wrong
No alerts: Stockouts happen without warning
Cost: 8-12% of revenue lost to poor inventory management
Common Scenarios:
1.🚫 Out of stock on website → Lost sales → Angry customers → Competitor wins
2.📦 Overstock → Tied-up capital → Storage costs → Dead stock write-offs
3.📊 Manual counts → 3 days of work → Still inaccurate
Solution: Real-Time Inventory System
Architectural Topology Specification
Modern Inventory Management Architecture — Shows:
E-Commerce Storefront → Inventory API
POS System → Inventory API
Marketplace Sync → Inventory API
Inventory API → Database (real-time stock)
Inventory API → Reorder Engine → Supplier alerts
Warehouse Management → Barcode scanning → Inventory updates
Core Data Model:
sqlProduction snippet
-- Products
CREATE TABLE products (
id UUID PRIMARY KEY,
sku VARCHAR(50) UNIQUE NOT NULL,
name VARCHAR(255) NOT NULL,
description TEXT,
price DECIMAL(10,2),
cost DECIMAL(10,2),
category VARCHAR(100),
weight DECIMAL(8,2),
created_at TIMESTAMP DEFAULT NOW()
);
-- Inventory
CREATE TABLE inventory (
id UUID PRIMARY KEY,
product_id UUID REFERENCES products(id),
location_id UUID REFERENCES locations(id),
quantity INT NOT NULL DEFAULT 0,
reserved_quantity INT NOT NULL DEFAULT 0,
available_quantity INT GENERATED ALWAYS AS (quantity - reserved_quantity) STORED,
reorder_point INT DEFAULT 5,
reorder_quantity INT DEFAULT 20,
last_updated TIMESTAMP DEFAULT NOW()
);
-- Inventory Transactions
CREATE TABLE inventory_transactions (
id UUID PRIMARY KEY,
product_id UUID REFERENCES products(id),
location_id UUID REFERENCES locations(id),
quantity_change INT NOT NULL,
transaction_type VARCHAR(50) NOT NULL, -- SALE, RESTOCK, RETURN, ADJUSTMENT
reference_id VARCHAR(100), -- Order ID, Purchase Order ID
note TEXT,
created_at TIMESTAMP DEFAULT NOW()
);
-- Locations (Warehouses, Stores)
CREATE TABLE locations (
id UUID PRIMARY KEY,
name VARCHAR(100) NOT NULL,
type VARCHAR(50) NOT NULL, -- WAREHOUSE, STORE, DC
address TEXT,
is_active BOOLEAN DEFAULT TRUE
);
Inventory API Implementation
Check Stock API
typescriptProduction snippet
// api/inventory/stock.ts
import { prisma } from "@/lib/db";
export async function getProductStock(productId: string) {
const inventory = await prisma.inventory.findMany({
where: { productId },
include: { location: true },
});
const totalStock = inventory.reduce(
(sum, item) => sum + item.quantity, 0
);
const availableStock = inventory.reduce(
(sum, item) => sum + (item.quantity - item.reservedQuantity), 0
);
return {
productId,
totalStock,
availableStock,
byLocation: inventory.map((item) => ({
location: item.location.name,
quantity: item.quantity,
available: item.quantity - item.reservedQuantity,
})),
};
}
Reserve Stock (for Orders)
typescriptProduction snippet
// api/inventory/reserve.ts
import { prisma } from "@/lib/db";
import { Prisma } from "@prisma/client";
export async function reserveStock(
productId: string,
quantity: number,
orderId: string
) {
return await prisma.$transaction(async (tx) => {
const inventory = await tx.inventory.findFirst({
where: { productId },
orderBy: { availableQuantity: "desc" },
});
if (!inventory) {
throw new Error("Product not found in inventory");
}
const available = inventory.quantity - inventory.reservedQuantity;
if (available < quantity) {
throw new Error(`Insufficient stock. Available: ${available}, Requested: ${quantity}`);
}
// Reserve the stock
await tx.inventory.update({
where: { id: inventory.id },
data: {
reservedQuantity: { increment: quantity },
},
});
// Create transaction record
await tx.inventoryTransaction.create({
data: {
productId,
locationId: inventory.locationId,
quantityChange: -quantity,
transactionType: "RESERVED",
referenceId: orderId,
note: `Reserved for order ${orderId}`,
},
});
return { success: true, reserved: quantity };
});
}
Release Stock (After Fulfillment)
typescriptProduction snippet
// api/inventory/release.ts
export async function releaseStock(
productId: string,
quantity: number,
orderId: string
) {
return await prisma.$transaction(async (tx) => {
const inventory = await tx.inventory.findFirst({
where: { productId },
});
if (!inventory) {
throw new Error("Product not found in inventory");
}
// Convert reserved to actual stock reduction
await tx.inventory.update({
where: { id: inventory.id },
data: {
quantity: { decrement: quantity },
reservedQuantity: { decrement: quantity },
},
});
await tx.inventoryTransaction.create({
data: {
productId,
locationId: inventory.locationId,
quantityChange: -quantity,
transactionType: "SALE",
referenceId: orderId,
note: `Fulfilled order ${orderId}`,
},
});
return { success: true };
});
}
Automated Reorder System
Reorder Engine
typescriptProduction snippet
// services/reorder.ts
import { prisma } from "@/lib/db";
export async function checkReorderPoints() {
const inventoryItems = await prisma.inventory.findMany({
where: {
quantity: {
lte: prisma.inventory.fields.reorderPoint,
},
},
include: {
product: true,
location: true,
},
});
const reorderItems: any[] = [];
for (const item of inventoryItems) {
const { product, quantity, reorderQuantity, reorderPoint, location } = item;
// Don't reorder if there are pending orders
const pendingOrders = await getPendingPurchaseOrders(product.id);
if (pendingOrders > 0) {
continue;
}
const quantityNeeded = reorderQuantity - (reorderPoint - quantity);
reorderItems.push({
product,
location,
currentStock: quantity,
reorderPoint,
quantityNeeded,
});
}
// Send reorder notifications
for (const item of reorderItems) {
await createPurchaseOrder(item);
await sendReorderAlert(item);
}
return reorderItems;
}
async function createPurchaseOrder(item: any) {
// Create purchase order in database
await prisma.purchaseOrder.create({
data: {
productId: item.product.id,
supplierId: item.product.supplierId,
quantity: item.quantityNeeded,
status: "PENDING",
notes: `Auto-generated reorder - stock below ${item.reorderPoint}`,
},
});
}
// Schedule reorder check every hour
import cron from "node-cron";
cron.schedule("0 * * * *", checkReorderPoints);
Lead Time Based Reordering
typescriptProduction snippet
export async function checkLeadTimeReorder() {
const products = await prisma.product.findMany({
where: { supplierLeadTime: { gt: 0 } },
include: { inventory: true },
});
for (const product of products) {
const totalStock = product.inventory.reduce(
(sum, inv) => sum + inv.quantity, 0
);
const dailySales = await getAverageDailySales(product.id);
const leadTimeDays = product.supplierLeadTime;
const bufferDays = 3; // Safety buffer
const minStock = dailySales * (leadTimeDays + bufferDays);
if (totalStock < minStock) {
const requiredQty = (minStock - totalStock) * 1.5;
await createPurchaseOrder({
product,
quantityNeeded: Math.ceil(requiredQty),
});
}
}
}
Multi-Location Synchronization
Architectural Topology Specification
Multi-Location Inventory Sync — Shows:
Warehouse A → Inventory Service
Warehouse B → Inventory Service
Store A → Inventory Service
Store B → Inventory Service
Inventory Service → Unified view → E-commerce → Customer
Finding Nearest Stock
typescriptProduction snippet
// api/inventory/nearest.ts
export async function findNearestLocation(
productId: string,
userLat: number,
userLng: number,
) {
const locations = await prisma.$queryRaw`
SELECT
l.id,
l.name,
l.latitude,
l.longitude,
i.quantity,
i.available_quantity,
(
6371 * acos(
cos(radians(${userLat})) *
cos(radians(l.latitude)) *
cos(radians(l.longitude) - radians(${userLng})) +
sin(radians(${userLat})) *
sin(radians(l.latitude))
)
) AS distance_km
FROM locations l
JOIN inventory i ON i.location_id = l.id
WHERE i.product_id = ${productId}
AND i.available_quantity > 0
ORDER BY distance_km ASC
LIMIT 1
`;
return locations[0] || null;
}
Integration with Sales Channels
Webhook for Real-Time Sync
typescriptProduction snippet
// api/webhooks/sale.ts
export async function handleSaleWebhook(req: Request) {
const { productId, quantity, channel } = await req.json();
// Update inventory in real-time
await prisma.inventory.update({
where: { productId },
data: {
quantity: { decrement: quantity },
},
});
// Sync to all channels
await syncToChannels({ productId, quantity, channel });
return Response.json({ success: true });
}
async function syncToChannels(data: any) {
const channels = await getActiveChannels();
for (const channel of channels) {
try {
await fetch(`${channel.webhookUrl}/inventory`, {
method: "POST",
headers: { Authorization: `Bearer ${channel.apiKey}` },
body: JSON.stringify(data),
});
} catch (error) {
console.error(`Failed to sync to ${channel.name}:`, error);
}
}
}
Keywords:#inventory management#stock control#warehouse software#SKU tracking#reorder alerts#automation
GE
Guvantic Engineering
Engineering & System Architecture Division
Engineering teams at Guvantic deliver mission-critical software, high-concurrency microservices, and international organic positioning strategies for fast-growing companies in USA, UK, and India.