-- =====================================================
-- WAREHOUSE STRUCTURE
-- =====================================================
-- Warehouses (Main entity)
CREATE TABLE warehouses (
id TEXT PRIMARY KEY,
code TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
city TEXT NOT NULL,
country TEXT NOT NULL,
surface REAL, -- square meters
capacity INTEGER,
used_capacity INTEGER DEFAULT 0,
zone_count INTEGER DEFAULT 0,
picker_count INTEGER DEFAULT 0,
manager TEXT,
email TEXT,
phone TEXT,
status TEXT NOT NULL, -- 'active', 'inactive', 'maintenance'
opening_date TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
-- Zones (Hierarchical: belongs to warehouse)
CREATE TABLE zones (
id TEXT PRIMARY KEY,
warehouse_id TEXT NOT NULL,
code TEXT NOT NULL,
name TEXT NOT NULL,
type TEXT NOT NULL, -- 'storage', 'receiving', 'shipping', 'picking', 'packing', 'cold_storage', 'hazardous'
surface REAL,
capacity INTEGER,
used_capacity INTEGER DEFAULT 0,
sector_count INTEGER DEFAULT 0,
location_count INTEGER DEFAULT 0,
picker_count INTEGER DEFAULT 0,
temperature_min REAL,
temperature_max REAL,
status TEXT NOT NULL, -- 'active', 'inactive', 'maintenance', 'full'
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
FOREIGN KEY (warehouse_id) REFERENCES warehouses(id) ON DELETE CASCADE
);
-- Sectors (Hierarchical: belongs to zone -> warehouse)
CREATE TABLE sectors (
id TEXT PRIMARY KEY,
warehouse_id TEXT NOT NULL,
zone_id TEXT NOT NULL,
code TEXT NOT NULL,
name TEXT NOT NULL,
type TEXT NOT NULL, -- 'rack', 'shelf', 'floor', 'bin', 'mezzanine'
capacity INTEGER,
used_capacity INTEGER DEFAULT 0,
location_count INTEGER DEFAULT 0,
picker_count INTEGER DEFAULT 0,
aisle TEXT,
level INTEGER,
position TEXT,
status TEXT NOT NULL, -- 'active', 'inactive', 'maintenance', 'full'
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
FOREIGN KEY (warehouse_id) REFERENCES warehouses(id) ON DELETE CASCADE,
FOREIGN KEY (zone_id) REFERENCES zones(id) ON DELETE CASCADE
);
-- Locations (Hierarchical: belongs to sector -> zone -> warehouse)
CREATE TABLE locations (
id TEXT PRIMARY KEY,
warehouse_id TEXT NOT NULL,
zone_id TEXT NOT NULL,
sector_id TEXT NOT NULL,
code TEXT NOT NULL,
type TEXT NOT NULL, -- 'rack', 'shelf', 'floor', 'bin', 'pallet'
capacity INTEGER,
used_capacity INTEGER DEFAULT 0,
product_count INTEGER DEFAULT 0,
picker_count INTEGER DEFAULT 0,
aisle TEXT,
level INTEGER,
position TEXT,
barcode TEXT,
status TEXT NOT NULL, -- 'available', 'occupied', 'blocked', 'reserved', 'full'
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
FOREIGN KEY (warehouse_id) REFERENCES warehouses(id) ON DELETE CASCADE,
FOREIGN KEY (zone_id) REFERENCES zones(id) ON DELETE CASCADE,
FOREIGN KEY (sector_id) REFERENCES sectors(id) ON DELETE CASCADE
);
-- =====================================================
-- PRODUCTS & INVENTORY
-- =====================================================
-- Products (Catalog - global across all warehouses)
CREATE TABLE products (
id TEXT PRIMARY KEY,
sku TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
description TEXT,
category TEXT NOT NULL,
subcategory TEXT,
brand TEXT,
unit TEXT NOT NULL,
weight REAL, -- kg
volume REAL, -- cubic meters
min_stock INTEGER DEFAULT 0,
max_stock INTEGER,
reorder_point INTEGER,
reorder_quantity INTEGER,
cost_price REAL,
selling_price REAL,
supplier TEXT,
status TEXT NOT NULL, -- 'in_stock', 'low_stock', 'out_of_stock', 'discontinued'
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
-- Inventory (Stock levels by product and location - CRITICAL TABLE)
CREATE TABLE inventory (
id TEXT PRIMARY KEY,
warehouse_id TEXT NOT NULL,
product_id TEXT NOT NULL,
location_id TEXT, -- NULL = unknown location
quantity INTEGER NOT NULL DEFAULT 0,
available_quantity INTEGER NOT NULL DEFAULT 0, -- quantity - reserved_quantity
reserved_quantity INTEGER NOT NULL DEFAULT 0,
last_received_at TEXT,
last_shipped_at TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
FOREIGN KEY (warehouse_id) REFERENCES warehouses(id) ON DELETE CASCADE,
FOREIGN KEY (product_id) REFERENCES products(id) ON DELETE CASCADE,
FOREIGN KEY (location_id) REFERENCES locations(id) ON DELETE SET NULL,
UNIQUE(warehouse_id, product_id, location_id)
);
-- =====================================================
-- ORDERS (Sales orders)
-- =====================================================
-- Orders
CREATE TABLE orders (
id TEXT PRIMARY KEY,
warehouse_id TEXT NOT NULL,
order_number TEXT NOT NULL,
customer_id TEXT NOT NULL,
customer_name TEXT NOT NULL,
customer_email TEXT,
order_date TEXT NOT NULL,
required_date TEXT NOT NULL,
promised_date TEXT,
shipped_date TEXT,
delivered_date TEXT,
status TEXT NOT NULL, -- 'pending', 'confirmed', 'picking', 'picked', 'packing', 'packed', 'shipped', 'delivered', 'cancelled'
priority TEXT NOT NULL, -- 'low', 'medium', 'high', 'urgent'
total_quantity INTEGER DEFAULT 0,
total_amount REAL DEFAULT 0,
shipping_address TEXT,
shipping_city TEXT,
shipping_country TEXT,
tracking_number TEXT,
carrier TEXT,
notes TEXT,
picker TEXT,
packer TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
FOREIGN KEY (warehouse_id) REFERENCES warehouses(id) ON DELETE CASCADE
);
-- Order Lines
CREATE TABLE order_lines (
id TEXT PRIMARY KEY,
order_id TEXT NOT NULL,
warehouse_id TEXT NOT NULL,
product_id TEXT NOT NULL,
product_sku TEXT NOT NULL,
product_name TEXT NOT NULL,
quantity INTEGER NOT NULL,
picked_quantity INTEGER DEFAULT 0,
unit_price REAL NOT NULL,
total_price REAL NOT NULL,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE,
FOREIGN KEY (warehouse_id) REFERENCES warehouses(id) ON DELETE CASCADE,
FOREIGN KEY (product_id) REFERENCES products(id)
);
-- =====================================================
-- PICKING OPERATIONS
-- =====================================================
-- Pickings (Picking operations linked to orders)
CREATE TABLE pickings (
id TEXT PRIMARY KEY,
warehouse_id TEXT NOT NULL,
order_id TEXT NOT NULL,
order_number TEXT NOT NULL,
customer_id TEXT NOT NULL,
customer_name TEXT NOT NULL,
picking_number TEXT NOT NULL,
assigned_date TEXT NOT NULL,
started_date TEXT,
completed_date TEXT,
status TEXT NOT NULL, -- 'pending', 'in_progress', 'completed', 'partial', 'cancelled'
priority TEXT NOT NULL, -- 'low', 'medium', 'high', 'urgent'
total_quantity INTEGER DEFAULT 0,
picked_quantity INTEGER DEFAULT 0,
remaining_quantity INTEGER DEFAULT 0,
picker TEXT,
picker_id TEXT,
equipment TEXT,
notes TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
FOREIGN KEY (warehouse_id) REFERENCES warehouses(id) ON DELETE CASCADE,
FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE
);
-- Picking Lines
CREATE TABLE picking_lines (
id TEXT PRIMARY KEY,
picking_id TEXT NOT NULL,
warehouse_id TEXT NOT NULL,
product_id TEXT NOT NULL,
product_sku TEXT NOT NULL,
product_name TEXT NOT NULL,
location_code TEXT NOT NULL,
zone_name TEXT,
quantity INTEGER NOT NULL,
picked_quantity INTEGER DEFAULT 0,
unit TEXT NOT NULL,
status TEXT NOT NULL, -- 'pending', 'picked', 'partial'
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
FOREIGN KEY (picking_id) REFERENCES pickings(id) ON DELETE CASCADE,
FOREIGN KEY (warehouse_id) REFERENCES warehouses(id) ON DELETE CASCADE,
FOREIGN KEY (product_id) REFERENCES products(id)
);
-- =====================================================
-- RECEIPTS (Supplier receipts)
-- =====================================================
-- Receptions
CREATE TABLE receptions (
id TEXT PRIMARY KEY,
warehouse_id TEXT NOT NULL,
reception_number TEXT NOT NULL,
supplier_id TEXT NOT NULL,
supplier_name TEXT NOT NULL,
purchase_order_number TEXT,
expected_date TEXT NOT NULL,
received_date TEXT,
status TEXT NOT NULL, -- 'pending', 'in_progress', 'completed', 'partial', 'cancelled'
priority TEXT NOT NULL, -- 'low', 'medium', 'high', 'urgent'
total_quantity INTEGER DEFAULT 0,
received_quantity INTEGER DEFAULT 0,
rejected_quantity INTEGER DEFAULT 0,
total_amount REAL DEFAULT 0,
carrier TEXT,
tracking_number TEXT,
dock_door TEXT,
receiver TEXT,
notes TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
FOREIGN KEY (warehouse_id) REFERENCES warehouses(id) ON DELETE CASCADE
);
-- Reception Lines
CREATE TABLE reception_lines (
id TEXT PRIMARY KEY,
reception_id TEXT NOT NULL,
warehouse_id TEXT NOT NULL,
product_id TEXT NOT NULL,
product_sku TEXT NOT NULL,
product_name TEXT NOT NULL,
ordered_quantity INTEGER NOT NULL,
received_quantity INTEGER DEFAULT 0,
rejected_quantity INTEGER DEFAULT 0,
unit_price REAL NOT NULL,
total_price REAL NOT NULL,
reason TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
FOREIGN KEY (reception_id) REFERENCES receptions(id) ON DELETE CASCADE,
FOREIGN KEY (warehouse_id) REFERENCES warehouses(id) ON DELETE CASCADE,
FOREIGN KEY (product_id) REFERENCES products(id)
);
-- =====================================================
-- RESTOCKING (Replenishment)
-- =====================================================
-- Restockings
CREATE TABLE restockings (
id TEXT PRIMARY KEY,
warehouse_id TEXT NOT NULL,
restocking_number TEXT NOT NULL,
status TEXT NOT NULL, -- 'pending', 'in_progress', 'completed', 'cancelled'
priority TEXT NOT NULL, -- 'low', 'medium', 'high', 'urgent'
total_products INTEGER DEFAULT 0,
restocked_products INTEGER DEFAULT 0,
requester TEXT NOT NULL,
assigned_to TEXT,
requested_date TEXT NOT NULL,
started_date TEXT,
completed_date TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
FOREIGN KEY (warehouse_id) REFERENCES warehouses(id) ON DELETE CASCADE
);
-- Restocking Lines
CREATE TABLE restocking_lines (
id TEXT PRIMARY KEY,
restocking_id TEXT NOT NULL,
warehouse_id TEXT NOT NULL,
product_id TEXT NOT NULL,
product_sku TEXT NOT NULL,
product_name TEXT NOT NULL,
current_quantity INTEGER NOT NULL,
target_quantity INTEGER NOT NULL,
quantity_to_restock INTEGER NOT NULL,
unit TEXT NOT NULL,
status TEXT NOT NULL, -- 'pending', 'in_progress', 'completed'
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
FOREIGN KEY (restocking_id) REFERENCES restockings(id) ON DELETE CASCADE,
FOREIGN KEY (warehouse_id) REFERENCES warehouses(id) ON DELETE CASCADE,
FOREIGN KEY (product_id) REFERENCES products(id)
);
-- =====================================================
-- RETURNS
-- =====================================================
-- Returns
CREATE TABLE returns (
id TEXT PRIMARY KEY,
warehouse_id TEXT NOT NULL,
order_id TEXT,
order_number TEXT,
return_number TEXT NOT NULL,
customer_id TEXT NOT NULL,
customer_name TEXT NOT NULL,
return_date TEXT NOT NULL,
type TEXT NOT NULL, -- 'customer', 'supplier', 'damage', 'defective', 'expired'
status TEXT NOT NULL, -- 'pending', 'in_progress', 'completed', 'received', 'inspecting', 'approved', 'rejected', 'refunded', 'restocked', 'disposed'
priority TEXT NOT NULL, -- 'low', 'medium', 'high', 'urgent'
reason TEXT NOT NULL, -- 'damaged', 'defective', 'product_defective', 'wrong_item', 'not_as_described', 'no_longer_needed', 'expired', 'quality_issue', 'other'
reason_label TEXT NOT NULL,
total_quantity INTEGER DEFAULT 0,
total_amount REAL DEFAULT 0,
refunded_amount REAL DEFAULT 0,
processor TEXT,
completed_date TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
FOREIGN KEY (warehouse_id) REFERENCES warehouses(id) ON DELETE CASCADE,
FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE SET NULL
);
-- Return Lines
CREATE TABLE return_lines (
id TEXT PRIMARY KEY,
return_id TEXT NOT NULL,
warehouse_id TEXT NOT NULL,
product_id TEXT NOT NULL,
product_sku TEXT NOT NULL,
product_name TEXT NOT NULL,
quantity INTEGER NOT NULL,
unit_price REAL NOT NULL,
total_price REAL NOT NULL,
condition TEXT NOT NULL, -- 'new', 'good', 'fair', 'poor', 'damaged'
resolution TEXT NOT NULL, -- 'refund', 'exchange', 'repair', 'dispose'
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
FOREIGN KEY (return_id) REFERENCES returns(id) ON DELETE CASCADE,
FOREIGN KEY (warehouse_id) REFERENCES warehouses(id) ON DELETE CASCADE,
FOREIGN KEY (product_id) REFERENCES products(id)
);
-- =====================================================
-- MOVEMENTS (Historical stock movements)
-- =====================================================
-- Movements (All inventory movements - inbound, outbound, transfers, adjustments)
CREATE TABLE movements (
id TEXT PRIMARY KEY,
warehouse_id TEXT NOT NULL,
product_id TEXT NOT NULL,
product_sku TEXT NOT NULL,
product_name TEXT NOT NULL,
type TEXT NOT NULL, -- 'inbound', 'outbound', 'transfer', 'adjustment'
source_location_id TEXT,
source_zone TEXT,
source_location_code TEXT,
destination_location_id TEXT,
destination_zone TEXT,
destination_location_code TEXT,
quantity INTEGER NOT NULL,
unit TEXT NOT NULL,
movement_date TEXT NOT NULL,
user TEXT,
reason TEXT,
lot TEXT,
expiration_date TEXT,
reference_type TEXT, -- 'order', 'reception', 'picking', 'restocking', 'return', 'adjustment'
reference_id TEXT, -- ID of the related document
created_at TEXT NOT NULL DEFAULT (datetime('now')),
FOREIGN KEY (warehouse_id) REFERENCES warehouses(id) ON DELETE CASCADE,
FOREIGN KEY (product_id) REFERENCES products(id),
FOREIGN KEY (source_location_id) REFERENCES locations(id) ON DELETE SET NULL,
FOREIGN KEY (destination_location_id) REFERENCES locations(id) ON DELETE SET NULL
);
-- =====================================================
-- USERS (Warehouse operators)
-- =====================================================
-- Users
CREATE TABLE users (
id TEXT PRIMARY KEY,
warehouse_id TEXT NOT NULL,
username TEXT NOT NULL UNIQUE,
full_name TEXT NOT NULL,
email TEXT UNIQUE,
role TEXT NOT NULL, -- 'admin', 'manager', 'picker', 'packer', 'receiver', 'operator'
status TEXT NOT NULL, -- 'active', 'inactive'
last_login_at TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
FOREIGN KEY (warehouse_id) REFERENCES warehouses(id) ON DELETE CASCADE
);
-- =====================================================
-- IMPORT HISTORY
-- =====================================================
-- Import History
CREATE TABLE import_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
warehouse_id TEXT NOT NULL,
plugin_id TEXT NOT NULL,
plugin_version TEXT NOT NULL,
imported_at TEXT NOT NULL DEFAULT (datetime('now')),
rows_processed INTEGER NOT NULL,
status TEXT NOT NULL, -- 'success', 'partial', 'failed'
file_name TEXT,
file_size INTEGER,
duration_ms INTEGER,
error_message TEXT,
FOREIGN KEY (warehouse_id) REFERENCES warehouses(id) ON DELETE CASCADE
);
-- =====================================================
-- INDEXES (Performance critical)
-- =====================================================
-- Warehouse indexes
CREATE INDEX idx_zones_warehouse ON zones(warehouse_id);
CREATE INDEX idx_sectors_warehouse ON sectors(warehouse_id);
CREATE INDEX idx_sectors_zone ON sectors(zone_id);
CREATE INDEX idx_locations_warehouse ON locations(warehouse_id);
CREATE INDEX idx_locations_zone ON locations(zone_id);
CREATE INDEX idx_locations_sector ON locations(sector_id);
-- Product & Inventory indexes
CREATE INDEX idx_products_sku ON products(sku);
CREATE INDEX idx_products_category ON products(category);
CREATE INDEX idx_inventory_warehouse ON inventory(warehouse_id);
CREATE INDEX idx_inventory_product ON inventory(product_id);
CREATE INDEX idx_inventory_location ON inventory(location_id);
CREATE INDEX idx_inventory_warehouse_product ON inventory(warehouse_id, product_id);
-- Order indexes
CREATE INDEX idx_orders_warehouse ON orders(warehouse_id);
CREATE INDEX idx_orders_status ON orders(status);
CREATE INDEX idx_orders_date ON orders(order_date);
CREATE INDEX idx_order_lines_order ON order_lines(order_id);
CREATE INDEX idx_order_lines_product ON order_lines(product_id);
-- Picking indexes
CREATE INDEX idx_pickings_warehouse ON pickings(warehouse_id);
CREATE INDEX idx_pickings_status ON pickings(status);
CREATE INDEX idx_pickings_order ON pickings(order_id);
CREATE INDEX idx_picking_lines_picking ON picking_lines(picking_id);
-- Reception indexes
CREATE INDEX idx_receptions_warehouse ON receptions(warehouse_id);
CREATE INDEX idx_receptions_status ON receptions(status);
CREATE INDEX idx_reception_lines_reception ON reception_lines(reception_id);
-- Restocking indexes
CREATE INDEX idx_restockings_warehouse ON restockings(warehouse_id);
CREATE INDEX idx_restockings_status ON restockings(status);
CREATE INDEX idx_restocking_lines_restocking ON restocking_lines(restocking_id);
-- Return indexes
CREATE INDEX idx_returns_warehouse ON returns(warehouse_id);
CREATE INDEX idx_returns_status ON returns(status);
CREATE INDEX idx_return_lines_return ON return_lines(return_id);
-- Movement indexes (CRITICAL for analytics)
CREATE INDEX idx_movements_warehouse ON movements(warehouse_id);
CREATE INDEX idx_movements_product ON movements(product_id);
CREATE INDEX idx_movements_date ON movements(movement_date);
CREATE INDEX idx_movements_type ON movements(type);
CREATE INDEX idx_movements_warehouse_product ON movements(warehouse_id, product_id);
CREATE INDEX idx_movements_warehouse_date ON movements(warehouse_id, movement_date);
-- User indexes
CREATE INDEX idx_users_warehouse ON users(warehouse_id);
CREATE INDEX idx_users_username ON users(username);
-- Import history indexes
CREATE INDEX idx_import_history_warehouse ON import_history(warehouse_id);
CREATE INDEX idx_import_history_date ON import_history(imported_at);
Implement Backend Import System with SQLite and Plugin Architecture
Current State
The application currently has:
src/types/entities.tsWhat's missing:
Problem
We need to implement a complete backend system to:
The system must be:
Requirements
1. Database Schema
Global tables approach with warehouse_id as explicit filter (not automatic, not prefixed):
Critical constraint: NO automatic warehouse_id filtering. All queries must explicitly filter by warehouse_id passed as parameter. This design supports multiple warehouses in a single database while maintaining data isolation through explicit filtering.
2. Plugin System Architecture
All plugins must produce the same normalized output schema matching the database structure:
Plugin registry using Record type (not classes):
Functions only (no methods):
3. Import Workflow
User selects plugin manually (NO auto-detection)
User uploads Excel file
Validation
Preview (optional)
Select warehouse
Execute import
xlsxlibrarySummary
4. Functional Programming Constraints
Strict requirements:
type, notinterface)readonlyproperties for immutabilityExamples:
5. Directory Structure to Create
6. IPC Communication
Preload script exposes functions to renderer:
Main process IPC handlers (in
electron/main.mjs):7. Database Query Functions
Critical: All query functions MUST require warehouse_id explicitly:
8. Dependencies to Install
{ "dependencies": { "better-sqlite3": "^9.0.0", "xlsx": "^0.18.5" } }Note:
better-sqlite3is synchronous (better control),xlsxfor Excel parsing.9. Implementation Phases
Phase 1: Database Foundation
Phase 2: Plugin System
Phase 3: Import Engine
Phase 4: UI Integration
Phase 5: Concrete Plugin
Success Criteria
Out of Scope
For this initial implementation:
References
docs/features/02-technical-architecture.mddocs/features/11-import-plugins.mddocs/features/16-complete-data-flow.mdsrc/types/entities.ts