Version : 1.0.0
Last Updated : December 29, 2025
Database : PostgreSQL 16
Total Tables : 51
This appendix contains the complete Entity Relationship Diagram (ERD) for the POS Platform database. The schema is organized by domain with a schema-per-tenant multi-tenancy model.
pos_platform (database)
|
+-- shared (schema)
| Contains: tenants, modules, system settings
|
+-- tenant_nexus (schema per tenant)
| Contains: All tenant-specific tables
|
+-- tenant_retailco (schema per tenant)
Contains: All tenant-specific tables
╔═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗
║ POS PLATFORM - COMPLETE ENTITY RELATIONSHIP DIAGRAM ║
║ 51 Tables | 14 Domains ║
╠═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════╣
║ ║
║ ╔══════════════════════════════════════════════════════════════════════════════════════════════════════════════╗ ║
║ ║ DOMAIN 1: MULTI-TENANCY (shared schema) ║ ║
║ ╠══════════════════════════════════════════════════════════════════════════════════════════════════════════════╣ ║
║ ║ ║ ║
║ ║ ┌──────────────────────────┐ ┌──────────────────────────┐ ║ ║
║ ║ │ tenants │ │ tenant_modules │ ║ ║
║ ║ ├──────────────────────────┤ ├──────────────────────────┤ ║ ║
║ ║ │ PK id UUID │───────────│ PK id UUID │ ║ ║
║ ║ │ name VARCHAR(100) │ 1:N │ FK tenant_id UUID │──┐ ║ ║
║ ║ │ subdomain VARCHAR(50) │ │ module_code VARCHAR │ │ ║ ║
║ ║ │ status ENUM │ │ enabled BOOLEAN │ │ ┌──────────────────────────┐ ║ ║
║ ║ │ plan ENUM │ │ config JSONB │ │ │ system_settings │ ║ ║
║ ║ │ schema_name VARCHAR │ │ activated_at TIMESTP │ │ ├──────────────────────────┤ ║ ║
║ ║ │ settings JSONB │ └──────────────────────────┘ ├────►│ PK id UUID │ ║ ║
║ ║ │ created_at TIMESTAMP │ │ │ FK tenant_id UUID │ ║ ║
║ ║ │ updated_at TIMESTAMP │ │ │ key VARCHAR(100) │ ║ ║
║ ║ └──────────────────────────┘ │ │ value JSONB │ ║ ║
║ ║ │ │ updated_at TIMESTAMP │ ║ ║
║ ║ │ └──────────────────────────┘ ║ ║
║ ╚══════════════════════════════════════════════════════════════════════════════════════════════════════════════╝ ║
║ │ ║
║ │ tenant_id (implicit via schema) ║
║ ▼ ║
║ ╔══════════════════════════════════════════════════════════════════════════════════════════════════════════════╗ ║
║ ║ DOMAIN 2: LOCATIONS & REGISTERS ║ ║
║ ╠══════════════════════════════════════════════════════════════════════════════════════════════════════════════╣ ║
║ ║ ║ ║
║ ║ ┌──────────────────────────┐ ┌──────────────────────────┐ ┌──────────────────────────┐ ║ ║
║ ║ │ locations │ │ registers │ │ operating_hours │ ║ ║
║ ║ ├──────────────────────────┤ ├──────────────────────────┤ ├──────────────────────────┤ ║ ║
║ ║ │ PK id UUID │──────────►│ PK id UUID │ │ PK id UUID │ ║ ║
║ ║ │ code VARCHAR(10) │ 1:N │ FK location_id UUID │ │ FK location_id UUID │◄──┤ ║ ║
║ ║ │ name VARCHAR(100) │ │ name VARCHAR(50) │ │ day_of_week INT │ ║ ║
║ ║ │ type ENUM │ │ status ENUM │ │ open_time TIME │ ║ ║
║ ║ │ status ENUM │ │ terminal_id VARCHAR │ │ close_time TIME │ ║ ║
║ ║ │ address_line1 VARCHAR │ │ last_active TIMESTAMP │ │ is_closed BOOLEAN │ ║ ║
║ ║ │ address_line2 VARCHAR │ │ config JSONB │ └──────────────────────────┘ ║ ║
║ ║ │ city VARCHAR(100) │ └──────────────────────────┘ ║ ║
║ ║ │ state VARCHAR(50) │ ║ ║
║ ║ │ zip VARCHAR(20) │ ║ ║
║ ║ │ country VARCHAR(2) │ ║ ║
║ ║ │ phone VARCHAR(20) │ ║ ║
║ ║ │ timezone VARCHAR(50) │ ║ ║
║ ║ │ shopify_location_id │ ║ ║
║ ║ │ settings JSONB │ ║ ║
║ ║ │ created_at TIMESTAMP │ ║ ║
║ ║ └──────────────────────────┘ ║ ║
║ ║ │ ║ ║
║ ╚══════════════╪═══════════════════════════════════════════════════════════════════════════════════════════════╝ ║
║ │ ║
║ │ location_id ║
║ ▼ ║
║ ╔══════════════════════════════════════════════════════════════════════════════════════════════════════════════╗ ║
║ ║ DOMAIN 3: USERS & EMPLOYEES ║ ║
║ ╠══════════════════════════════════════════════════════════════════════════════════════════════════════════════╣ ║
║ ║ ║ ║
║ ║ ┌──────────────────────────┐ ┌──────────────────────────┐ ┌──────────────────────────┐ ║ ║
║ ║ │ users │ │ user_permissions │ │ user_sessions │ ║ ║
║ ║ ├──────────────────────────┤ ├──────────────────────────┤ ├──────────────────────────┤ ║ ║
║ ║ │ PK id UUID │──────────►│ PK id UUID │ │ PK id UUID │ ║ ║
║ ║ │ email VARCHAR(255) │ 1:N │ FK user_id UUID │ │ FK user_id UUID │◄──┤ ║ ║
║ ║ │ password_hash VARCHAR │ │ permission VARCHAR │ │ token_hash VARCHAR │ ║ ║
║ ║ │ first_name VARCHAR │ │ granted_by UUID │ │ device_info JSONB │ ║ ║
║ ║ │ last_name VARCHAR │ │ granted_at TIMESTAMP │ │ ip_address INET │ ║ ║
║ ║ │ role ENUM │ └──────────────────────────┘ │ expires_at TIMESTAMP │ ║ ║
║ ║ │ pin_hash VARCHAR │ │ created_at TIMESTAMP │ ║ ║
║ ║ │ FK home_location_id UUID │◄─────────────────────────────────────────────└──────────────────────────┘ ║ ║
║ ║ │ status ENUM │ ║ ║
║ ║ │ last_login TIMESTAMP │ ┌──────────────────────────┐ ║ ║
║ ║ │ created_at TIMESTAMP │ │ time_clock_entries │ ║ ║
║ ║ └──────────────────────────┘ ├──────────────────────────┤ ║ ║
║ ║ │ │ PK id UUID │ ║ ║
║ ║ │ │ FK user_id UUID │◄──────────────────────────────────────┤ ║ ║
║ ║ │ │ FK location_id UUID │ ║ ║
║ ║ │ │ clock_in TIMESTAMP │ ║ ║
║ ║ │ │ clock_out TIMESTAMP │ ║ ║
║ ║ │ │ break_minutes INT │ ║ ║
║ ║ │ │ status ENUM │ ║ ║
║ ║ │ │ notes TEXT │ ║ ║
║ ║ │ └──────────────────────────┘ ║ ║
║ ╚══════════════╪═══════════════════════════════════════════════════════════════════════════════════════════════╝ ║
║ │ ║
║ │ user_id ║
║ ▼ ║
║ ╔══════════════════════════════════════════════════════════════════════════════════════════════════════════════╗ ║
║ ║ DOMAIN 4: PRODUCTS & CATALOG ║ ║
║ ╠══════════════════════════════════════════════════════════════════════════════════════════════════════════════╣ ║
║ ║ ║ ║
║ ║ ┌──────────────────────────┐ ┌──────────────────────────┐ ┌──────────────────────────┐ ║ ║
║ ║ │ categories │ │ products │ │ product_variants │ ║ ║
║ ║ ├──────────────────────────┤ ├──────────────────────────┤ ├──────────────────────────┤ ║ ║
║ ║ │ PK id UUID │◄──────────│ PK id UUID │──────►│ PK id UUID │ ║ ║
║ ║ │ FK parent_id UUID (self) │ N:1 │ sku VARCHAR(50) │ 1:N │ FK product_id UUID │ ║ ║
║ ║ │ name VARCHAR(100) │ │ name VARCHAR(255) │ │ sku VARCHAR(50) │ ║ ║
║ ║ │ slug VARCHAR(100) │ │ description TEXT │ │ barcode VARCHAR(50) │ ║ ║
║ ║ │ sort_order INT │ │ FK category_id UUID │ │ options JSONB │ ║ ║
║ ║ │ is_active BOOLEAN │ │ FK vendor_id UUID │ │ price DECIMAL(10,2) │ ║ ║
║ ║ └──────────────────────────┘ │ base_price DECIMAL │ │ compare_price DECIMAL │ ║ ║
║ ║ │ cost DECIMAL(10,2) │ │ cost DECIMAL(10,2) │ ║ ║
║ ║ ┌──────────────────────────┐ │ tax_class VARCHAR │ │ weight DECIMAL │ ║ ║
║ ║ │ vendors │ │ status ENUM │ │ is_active BOOLEAN │ ║ ║
║ ║ ├──────────────────────────┤ │ shopify_product_id │ │ shopify_variant_id │ ║ ║
║ ║ │ PK id UUID │◄──────────│ created_at TIMESTAMP │ │ created_at TIMESTAMP │ ║ ║
║ ║ │ name VARCHAR(100) │ N:1 └──────────────────────────┘ └──────────────────────────┘ ║ ║
║ ║ │ code VARCHAR(20) │ │ │ ║ ║
║ ║ │ contact_name VARCHAR │ │ │ ║ ║
║ ║ │ email VARCHAR(255) │ │ │ ║ ║
║ ║ │ phone VARCHAR(20) │ ▼ ▼ ║ ║
║ ║ │ address JSONB │ ┌──────────────────────────┐ ┌──────────────────────────┐ ║ ║
║ ║ │ payment_terms VARCHAR │ │ product_images │ │ variant_prices │ ║ ║
║ ║ │ is_active BOOLEAN │ ├──────────────────────────┤ ├──────────────────────────┤ ║ ║
║ ║ └──────────────────────────┘ │ PK id UUID │ │ PK id UUID │ ║ ║
║ ║ │ FK product_id UUID │ │ FK variant_id UUID │ ║ ║
║ ║ │ url VARCHAR(500) │ │ FK price_list_id UUID │ ║ ║
║ ║ │ alt_text VARCHAR │ │ price DECIMAL(10,2) │ ║ ║
║ ║ │ position INT │ │ effective_from DATE │ ║ ║
║ ║ └──────────────────────────┘ │ effective_to DATE │ ║ ║
║ ║ └──────────────────────────┘ ║ ║
║ ╚══════════════════════════════════════════════════════════════════════════════════════════════════════════════╝ ║
║ │ ║
║ │ variant_id ║
║ ▼ ║
║ ╔══════════════════════════════════════════════════════════════════════════════════════════════════════════════╗ ║
║ ║ DOMAIN 5: INVENTORY ║ ║
║ ╠══════════════════════════════════════════════════════════════════════════════════════════════════════════════╣ ║
║ ║ ║ ║
║ ║ ┌──────────────────────────┐ ┌──────────────────────────┐ ┌──────────────────────────┐ ║ ║
║ ║ │ inventory_levels │ │ inventory_transactions │ │ inventory_reservations │ ║ ║
║ ║ ├──────────────────────────┤ ├──────────────────────────┤ ├──────────────────────────┤ ║ ║
║ ║ │ PK id UUID │ │ PK id UUID │ │ PK id UUID │ ║ ║
║ ║ │ FK variant_id UUID │◄──────────│ FK variant_id UUID │ │ FK variant_id UUID │◄──┤ ║ ║
║ ║ │ FK location_id UUID │ 1:N │ FK location_id UUID │ │ FK location_id UUID │ ║ ║
║ ║ │ on_hand INT │ │ transaction_type ENUM │ │ FK order_id UUID │ ║ ║
║ ║ │ available INT │ │ quantity INT │ │ quantity INT │ ║ ║
║ ║ │ reserved INT │ │ previous_qty INT │ │ expires_at TIMESTAMP │ ║ ║
║ ║ │ reorder_point INT │ │ new_qty INT │ │ status ENUM │ ║ ║
║ ║ │ reorder_qty INT │ │ reference_type VARCHAR│ │ created_at TIMESTAMP │ ║ ║
║ ║ │ bin_location VARCHAR │ │ reference_id UUID │ └──────────────────────────┘ ║ ║
║ ║ │ updated_at TIMESTAMP │ │ cost DECIMAL(10,2) │ ║ ║
║ ║ │ UK (variant_id, loc_id) │ │ notes TEXT │ ║ ║
║ ║ └──────────────────────────┘ │ FK created_by UUID │ ║ ║
║ ║ │ │ created_at TIMESTAMP │ ║ ║
║ ║ │ └──────────────────────────┘ ║ ║
║ ║ │ ║ ║
║ ║ │ ┌──────────────────────────┐ ┌──────────────────────────┐ ║ ║
║ ║ │ │ inventory_transfers │ │ transfer_line_items │ ║ ║
║ ║ │ ├──────────────────────────┤ ├──────────────────────────┤ ║ ║
║ ║ │ │ PK id UUID │──────►│ PK id UUID │ ║ ║
║ ║ │ │ FK from_location_id UUID │ 1:N │ FK transfer_id UUID │ ║ ║
║ ║ │ │ FK to_location_id UUID │ │ FK variant_id UUID │ ║ ║
║ ║ └──────────►│ status ENUM │ │ qty_requested INT │ ║ ║
║ ║ │ priority ENUM │ │ qty_shipped INT │ ║ ║
║ ║ │ tracking_number VARCH │ │ qty_received INT │ ║ ║
║ ║ │ carrier VARCHAR │ │ qty_damaged INT │ ║ ║
║ ║ │ FK requested_by UUID │ └──────────────────────────┘ ║ ║
║ ║ │ FK shipped_by UUID │ ║ ║
║ ║ │ FK received_by UUID │ ║ ║
║ ║ │ shipped_at TIMESTAMP │ ║ ║
║ ║ │ received_at TIMESTAMP │ ║ ║
║ ║ │ created_at TIMESTAMP │ ║ ║
║ ║ └──────────────────────────┘ ║ ║
║ ╚══════════════════════════════════════════════════════════════════════════════════════════════════════════════╝ ║
║ │ ║
║ │ variant_id, location_id ║
║ ▼ ║
║ ╔══════════════════════════════════════════════════════════════════════════════════════════════════════════════╗ ║
║ ║ DOMAIN 6: ORDERS & SALES ║ ║
║ ╠══════════════════════════════════════════════════════════════════════════════════════════════════════════════╣ ║
║ ║ ║ ║
║ ║ ┌──────────────────────────┐ ┌──────────────────────────┐ ┌──────────────────────────┐ ║ ║
║ ║ │ orders │ │ order_line_items │ │ order_discounts │ ║ ║
║ ║ ├──────────────────────────┤ ├──────────────────────────┤ ├──────────────────────────┤ ║ ║
║ ║ │ PK id UUID │──────────►│ PK id UUID │ │ PK id UUID │ ║ ║
║ ║ │ order_number VARCHAR │ 1:N │ FK order_id UUID │ │ FK order_id UUID │◄──┤ ║ ║
║ ║ │ receipt_number VARCHAR│ │ FK variant_id UUID │ │ FK line_item_id UUID │ ║ ║
║ ║ │ FK location_id UUID │ │ sku VARCHAR │ │ discount_type ENUM │ ║ ║
║ ║ │ FK register_id UUID │ │ name VARCHAR │ │ discount_value DECIMAL│ ║ ║
║ ║ │ FK customer_id UUID │ │ quantity INT │ │ discount_amount DECIM │ ║ ║
║ ║ │ FK created_by UUID │ │ unit_price DECIMAL │ │ code VARCHAR │ ║ ║
║ ║ │ status ENUM │ │ discount_amount DECIM │ │ reason VARCHAR │ ║ ║
║ ║ │ subtotal DECIMAL │ │ tax_amount DECIMAL │ └──────────────────────────┘ ║ ║
║ ║ │ discount_total DECIM │ │ line_total DECIMAL │ ║ ║
║ ║ │ tax_total DECIMAL │ │ cost DECIMAL │ ║ ║
║ ║ │ total DECIMAL(10,2) │ │ fulfillment_status EN │ ║ ║
║ ║ │ channel ENUM │ └──────────────────────────┘ ║ ║
║ ║ │ source VARCHAR │ │ ║ ║
║ ║ │ notes TEXT │ │ ║ ║
║ ║ │ metadata JSONB │ │ ║ ║
║ ║ │ voided_at TIMESTAMP │ │ ║ ║
║ ║ │ FK voided_by UUID │ ▼ ║ ║
║ ║ │ void_reason VARCHAR │ ┌──────────────────────────┐ ┌──────────────────────────┐ ║ ║
║ ║ │ created_at TIMESTAMP │ │ returns │ │ return_line_items │ ║ ║
║ ║ │ completed_at TIMESTP │ ├──────────────────────────┤ ├──────────────────────────┤ ║ ║
║ ║ └──────────────────────────┘ │ PK id UUID │──────►│ PK id UUID │ ║ ║
║ ║ │ │ return_number VARCHAR │ 1:N │ FK return_id UUID │ ║ ║
║ ║ │ │ FK original_order_id UUID│ │ FK original_line_id UUID │ ║ ║
║ ║ │ │ FK location_id UUID │ │ FK variant_id UUID │ ║ ║
║ ║ │ │ FK customer_id UUID │ │ quantity INT │ ║ ║
║ ║ │ │ FK processed_by UUID │ │ refund_amount DECIMAL │ ║ ║
║ ║ │ │ status ENUM │ │ reason ENUM │ ║ ║
║ ║ │ │ refund_total DECIMAL │ │ condition ENUM │ ║ ║
║ ║ │ │ refund_method ENUM │ │ restocked BOOLEAN │ ║ ║
║ ║ │ │ created_at TIMESTAMP │ └──────────────────────────┘ ║ ║
║ ║ │ └──────────────────────────┘ ║ ║
║ ╚══════════════╪═══════════════════════════════════════════════════════════════════════════════════════════════╝ ║
║ │ ║
║ │ order_id ║
║ ▼ ║
║ ╔══════════════════════════════════════════════════════════════════════════════════════════════════════════════╗ ║
║ ║ DOMAIN 7: PAYMENTS ║ ║
║ ╠══════════════════════════════════════════════════════════════════════════════════════════════════════════════╣ ║
║ ║ ║ ║
║ ║ ┌──────────────────────────┐ ┌──────────────────────────┐ ┌──────────────────────────┐ ║ ║
║ ║ │ payments │ │ payment_refunds │ │ payment_batches │ ║ ║
║ ║ ├──────────────────────────┤ ├──────────────────────────┤ ├──────────────────────────┤ ║ ║
║ ║ │ PK id UUID │──────────►│ PK id UUID │ │ PK id UUID │ ║ ║
║ ║ │ FK order_id UUID │ 1:N │ FK payment_id UUID │◄──────│ FK location_id UUID │ ║ ║
║ ║ │ payment_method ENUM │ │ FK return_id UUID │ N:1 │ batch_date DATE │ ║ ║
║ ║ │ amount DECIMAL(10,2) │ │ amount DECIMAL │ │ status ENUM │ ║ ║
║ ║ │ status ENUM │ │ status ENUM │ │ total_amount DECIMAL │ ║ ║
║ ║ │ authorization_code │ │ gateway_refund_id │ │ transaction_count INT │ ║ ║
║ ║ │ gateway_transaction_id│ │ created_at TIMESTAMP │ │ settled_at TIMESTAMP │ ║ ║
║ ║ │ card_brand VARCHAR │ └──────────────────────────┘ │ created_at TIMESTAMP │ ║ ║
║ ║ │ card_last_four VARCHAR│ └──────────────────────────┘ ║ ║
║ ║ │ entry_method ENUM │ │ ║ ║
║ ║ │ terminal_id VARCHAR │ │ ║ ║
║ ║ │ FK batch_id UUID │◄─────────────────────────────────────────────────────────┘ ║ ║
║ ║ │ tip_amount DECIMAL │ ║ ║
║ ║ │ metadata JSONB │ ║ ║
║ ║ │ created_at TIMESTAMP │ ║ ║
║ ║ └──────────────────────────┘ ║ ║
║ ╚══════════════════════════════════════════════════════════════════════════════════════════════════════════════╝ ║
║ ║
║ ╔══════════════════════════════════════════════════════════════════════════════════════════════════════════════╗ ║
║ ║ DOMAIN 8: CUSTOMERS & LOYALTY ║ ║
║ ╠══════════════════════════════════════════════════════════════════════════════════════════════════════════════╣ ║
║ ║ ║ ║
║ ║ ┌──────────────────────────┐ ┌──────────────────────────┐ ┌──────────────────────────┐ ║ ║
║ ║ │ customers │ │ loyalty_transactions │ │ customer_tags │ ║ ║
║ ║ ├──────────────────────────┤ ├──────────────────────────┤ ├──────────────────────────┤ ║ ║
║ ║ │ PK id UUID │──────────►│ PK id UUID │ │ PK id UUID │ ║ ║
║ ║ │ customer_number VARCH │ 1:N │ FK customer_id UUID │ │ FK customer_id UUID │◄──┤ ║ ║
║ ║ │ first_name VARCHAR │ │ FK order_id UUID │ │ FK tag_id UUID │ ║ ║
║ ║ │ last_name VARCHAR │ │ transaction_type ENUM │ │ applied_at TIMESTAMP │ ║ ║
║ ║ │ email VARCHAR(255) │ │ points INT │ │ expires_at TIMESTAMP │ ║ ║
║ ║ │ phone VARCHAR(20) │ │ balance_after INT │ │ applied_by UUID │ ║ ║
║ ║ │ address JSONB │ │ description VARCHAR │ └──────────────────────────┘ ║ ║
║ ║ │ loyalty_tier ENUM │ │ created_at TIMESTAMP │ ║ ║
║ ║ │ loyalty_points INT │ └──────────────────────────┘ ┌──────────────────────────┐ ║ ║
║ ║ │ lifetime_spend DECIM │ │ tags │ ║ ║
║ ║ │ total_orders INT │ ├──────────────────────────┤ ║ ║
║ ║ │ marketing_opt_in BOOL │ │ PK id UUID │ ║ ║
║ ║ │ sms_opt_in BOOLEAN │ ┌──────────────────────────┐ │ name VARCHAR(50) │ ║ ║
║ ║ │ tax_exempt BOOLEAN │ │ customer_notes │ │ category VARCHAR │ ║ ║
║ ║ │ notes TEXT │ ├──────────────────────────┤ │ color VARCHAR(7) │ ║ ║
║ ║ │ metadata JSONB │ │ PK id UUID │ │ is_auto BOOLEAN │ ║ ║
║ ║ │ created_at TIMESTAMP │──────────►│ FK customer_id UUID │ └──────────────────────────┘ ║ ║
║ ║ │ updated_at TIMESTAMP │ 1:N │ FK created_by UUID │ ║ ║
║ ║ └──────────────────────────┘ │ note TEXT │ ║ ║
║ ║ │ created_at TIMESTAMP │ ║ ║
║ ║ └──────────────────────────┘ ║ ║
║ ╚══════════════════════════════════════════════════════════════════════════════════════════════════════════════╝ ║
║ ║
║ ╔══════════════════════════════════════════════════════════════════════════════════════════════════════════════╗ ║
║ ║ DOMAIN 9: GIFT CARDS ║ ║
║ ╠══════════════════════════════════════════════════════════════════════════════════════════════════════════════╣ ║
║ ║ ║ ║
║ ║ ┌──────────────────────────┐ ┌──────────────────────────┐ ║ ║
║ ║ │ gift_cards │ │ gift_card_transactions │ ║ ║
║ ║ ├──────────────────────────┤ ├──────────────────────────┤ ║ ║
║ ║ │ PK id UUID │──────────►│ PK id UUID │ ║ ║
║ ║ │ card_number VARCHAR │ 1:N │ FK gift_card_id UUID │ ║ ║
║ ║ │ card_number_hash VARCH│ │ FK order_id UUID │ ║ ║
║ ║ │ initial_balance DECIM │ │ transaction_type ENUM │ ║ ║
║ ║ │ current_balance DECIM │ │ amount DECIMAL │ ║ ║
║ ║ │ status ENUM │ │ balance_after DECIMAL │ ║ ║
║ ║ │ type ENUM │ │ reference VARCHAR │ ║ ║
║ ║ │ purchased_at TIMESTAMP│ │ created_at TIMESTAMP │ ║ ║
║ ║ │ FK purchased_by UUID │ └──────────────────────────┘ ║ ║
║ ║ │ FK purchase_order_id UUID│ ║ ║
║ ║ │ recipient_email VARCH │ ║ ║
║ ║ │ recipient_name VARCHAR│ ║ ║
║ ║ │ message TEXT │ ║ ║
║ ║ │ expires_at TIMESTAMP │ ║ ║
║ ║ │ created_at TIMESTAMP │ ║ ║
║ ║ └──────────────────────────┘ ║ ║
║ ╚══════════════════════════════════════════════════════════════════════════════════════════════════════════════╝ ║
║ ║
║ ╔══════════════════════════════════════════════════════════════════════════════════════════════════════════════╗ ║
║ ║ DOMAIN 10: CASH MANAGEMENT ║ ║
║ ╠══════════════════════════════════════════════════════════════════════════════════════════════════════════════╣ ║
║ ║ ║ ║
║ ║ ┌──────────────────────────┐ ┌──────────────────────────┐ ┌──────────────────────────┐ ║ ║
║ ║ │ shifts │ │ cash_movements │ │ cash_counts │ ║ ║
║ ║ ├──────────────────────────┤ ├──────────────────────────┤ ├──────────────────────────┤ ║ ║
║ ║ │ PK id UUID │──────────►│ PK id UUID │ │ PK id UUID │ ║ ║
║ ║ │ FK register_id UUID │ 1:N │ FK shift_id UUID │ │ FK shift_id UUID │◄──┤ ║ ║
║ ║ │ FK opened_by UUID │ │ movement_type ENUM │ │ count_type ENUM │ ║ ║
║ ║ │ FK closed_by UUID │ │ amount DECIMAL │ │ expected DECIMAL │ ║ ║
║ ║ │ status ENUM │ │ FK performed_by UUID │ │ actual DECIMAL │ ║ ║
║ ║ │ opening_float DECIMAL │ │ FK witnessed_by UUID │ │ variance DECIMAL │ ║ ║
║ ║ │ expected_cash DECIMAL │ │ reason VARCHAR │ │ breakdown JSONB │ ║ ║
║ ║ │ actual_cash DECIMAL │ │ reference_number VARC │ │ FK counted_by UUID │ ║ ║
║ ║ │ variance DECIMAL │ │ notes TEXT │ │ counted_at TIMESTAMP │ ║ ║
║ ║ │ opened_at TIMESTAMP │ │ created_at TIMESTAMP │ │ notes TEXT │ ║ ║
║ ║ │ closed_at TIMESTAMP │ └──────────────────────────┘ └──────────────────────────┘ ║ ║
║ ║ │ notes TEXT │ ║ ║
║ ║ └──────────────────────────┘ ║ ║
║ ╚══════════════════════════════════════════════════════════════════════════════════════════════════════════════╝ ║
║ ║
║ ╔══════════════════════════════════════════════════════════════════════════════════════════════════════════════╗ ║
║ ║ DOMAIN 11: RFID ║ ║
║ ╠══════════════════════════════════════════════════════════════════════════════════════════════════════════════╣ ║
║ ║ ║ ║
║ ║ ┌──────────────────────────┐ ┌──────────────────────────┐ ┌──────────────────────────┐ ║ ║
║ ║ │ rfid_tags │ │ rfid_scan_sessions │ │ rfid_scans │ ║ ║
║ ║ ├──────────────────────────┤ ├──────────────────────────┤ ├──────────────────────────┤ ║ ║
║ ║ │ PK id UUID │ │ PK id UUID │──────►│ PK id UUID │ ║ ║
║ ║ │ epc VARCHAR(64) │ │ FK location_id UUID │ 1:N │ FK session_id UUID │ ║ ║
║ ║ │ FK variant_id UUID │ │ zone_id VARCHAR │ │ FK tag_id UUID │ ║ ║
║ ║ │ serial_number BIGINT │ │ session_type ENUM │ │ epc VARCHAR(64) │ ║ ║
║ ║ │ status ENUM │ │ FK started_by UUID │ │ rssi INT │ ║ ║
║ ║ │ FK current_location UUID │ │ FK completed_by UUID │ │ antenna_id INT │ ║ ║
║ ║ │ FK printed_at_location │ │ status ENUM │ │ read_count INT │ ║ ║
║ ║ │ printed_at TIMESTAMP │ │ started_at TIMESTAMP │ │ first_seen TIMESTAMP │ ║ ║
║ ║ │ FK printed_by UUID │ │ completed_at TIMESTAMP│ │ last_seen TIMESTAMP │ ║ ║
║ ║ │ last_seen_at TIMESTP │ │ summary JSONB │ └──────────────────────────┘ ║ ║
║ ║ │ created_at TIMESTAMP │ └──────────────────────────┘ ║ ║
║ ║ │ UK epc │ ║ ║
║ ║ └──────────────────────────┘ ║ ║
║ ╚══════════════════════════════════════════════════════════════════════════════════════════════════════════════╝ ║
║ ║
║ ╔══════════════════════════════════════════════════════════════════════════════════════════════════════════════╗ ║
║ ║ DOMAIN 12: EVENTS & SYNC ║ ║
║ ╠══════════════════════════════════════════════════════════════════════════════════════════════════════════════╣ ║
║ ║ ║ ║
║ ║ ┌──────────────────────────┐ ┌──────────────────────────┐ ┌──────────────────────────┐ ║ ║
║ ║ │ domain_events │ │ sync_queue │ │ conflict_resolutions │ ║ ║
║ ║ ├──────────────────────────┤ ├──────────────────────────┤ ├──────────────────────────┤ ║ ║
║ ║ │ PK id UUID │ │ PK id UUID │ │ PK id UUID │ ║ ║
║ ║ │ event_type VARCHAR │ │ device_id VARCHAR │ │ conflict_type VARCHAR │ ║ ║
║ ║ │ aggregate_type VARCHAR│ │ direction ENUM │ │ entity_type VARCHAR │ ║ ║
║ ║ │ aggregate_id UUID │ │ event_type VARCHAR │ │ entity_id UUID │ ║ ║
║ ║ │ payload JSONB │ │ payload JSONB │ │ server_value JSONB │ ║ ║
║ ║ │ correlation_id UUID │ │ local_sequence INT │ │ local_value JSONB │ ║ ║
║ ║ │ causation_id UUID │ │ status ENUM │ │ resolved_value JSONB │ ║ ║
║ ║ │ version INT │ │ attempts INT │ │ resolution_method EN │ ║ ║
║ ║ │ created_at TIMESTAMP │ │ last_attempt TIMESTP │ │ FK resolved_by UUID │ ║ ║
║ ║ │ IX (aggregate_type, id) │ │ error_message TEXT │ │ resolved_at TIMESTAMP │ ║ ║
║ ║ │ IX (created_at) │ │ created_at TIMESTAMP │ │ notes TEXT │ ║ ║
║ ║ └──────────────────────────┘ └──────────────────────────┘ └──────────────────────────┘ ║ ║
║ ╚══════════════════════════════════════════════════════════════════════════════════════════════════════════════╝ ║
║ ║
║ ╔══════════════════════════════════════════════════════════════════════════════════════════════════════════════╗ ║
║ ║ DOMAIN 13: AUDIT & LOGS ║ ║
║ ╠══════════════════════════════════════════════════════════════════════════════════════════════════════════════╣ ║
║ ║ ║ ║
║ ║ ┌──────────────────────────┐ ┌──────────────────────────┐ ║ ║
║ ║ │ audit_logs │ │ api_request_logs │ ║ ║
║ ║ ├──────────────────────────┤ ├──────────────────────────┤ ║ ║
║ ║ │ PK id UUID │ │ PK id UUID │ ║ ║
║ ║ │ action VARCHAR(50) │ │ method VARCHAR(10) │ ║ ║
║ ║ │ entity_type VARCHAR │ │ path VARCHAR(500) │ ║ ║
║ ║ │ entity_id UUID │ │ status_code INT │ ║ ║
║ ║ │ old_values JSONB │ │ duration_ms INT │ ║ ║
║ ║ │ new_values JSONB │ │ FK user_id UUID │ ║ ║
║ ║ │ FK performed_by UUID │ │ ip_address INET │ ║ ║
║ ║ │ ip_address INET │ │ user_agent VARCHAR │ ║ ║
║ ║ │ user_agent VARCHAR │ │ request_body JSONB │ ║ ║
║ ║ │ created_at TIMESTAMP │ │ created_at TIMESTAMP │ ║ ║
║ ║ │ IX (entity_type, id) │ │ IX (created_at) │ ║ ║
║ ║ │ IX (performed_by) │ │ IX (user_id) │ ║ ║
║ ║ │ IX (created_at) │ └──────────────────────────┘ ║ ║
║ ║ └──────────────────────────┘ ║ ║
║ ╚══════════════════════════════════════════════════════════════════════════════════════════════════════════════╝ ║
║ ║
╚═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝
Domain Tables Primary Tables
1. Multi-Tenancy 3 tenants, tenant_modules, system_settings
2. Locations 3 locations, registers, operating_hours
3. Users 4 users, user_permissions, user_sessions, time_clock_entries
4. Products 5 categories, vendors, products, product_variants, product_images, variant_prices
5. Inventory 5 inventory_levels, inventory_transactions, inventory_reservations, inventory_transfers, transfer_line_items
6. Orders 6 orders, order_line_items, order_discounts, returns, return_line_items
7. Payments 3 payments, payment_refunds, payment_batches
8. Customers 5 customers, loyalty_transactions, customer_tags, tags, customer_notes
9. Gift Cards 2 gift_cards, gift_card_transactions
10. Cash 3 shifts, cash_movements, cash_counts
11. RFID 3 rfid_tags, rfid_scan_sessions, rfid_scans
12. Events 3 domain_events, sync_queue, conflict_resolutions
13. Audit 2 audit_logs, api_request_logs
TOTAL 51
Parent Child Foreign Key
tenants tenant_modules tenant_id
locations registers location_id
locations operating_hours location_id
users user_permissions user_id
users user_sessions user_id
users time_clock_entries user_id
categories categories (self) parent_id
categories products category_id
vendors products vendor_id
products product_variants product_id
products product_images product_id
product_variants inventory_levels variant_id
product_variants inventory_transactions variant_id
product_variants order_line_items variant_id
orders order_line_items order_id
orders order_discounts order_id
orders payments order_id
orders returns original_order_id
returns return_line_items return_id
payments payment_refunds payment_id
payment_batches payments batch_id
customers orders customer_id
customers loyalty_transactions customer_id
customers customer_tags customer_id
customers customer_notes customer_id
gift_cards gift_card_transactions gift_card_id
shifts cash_movements shift_id
shifts cash_counts shift_id
inventory_transfers transfer_line_items transfer_id
rfid_scan_sessions rfid_scans session_id
Table A Junction Table B
customers customer_tags tags
product_variants variant_prices price_lists
-- Orders lookup
CREATE INDEX idx_orders_location_date ON orders(location_id, created_at DESC);
CREATE INDEX idx_orders_customer ON orders(customer_id);
CREATE INDEX idx_orders_receipt ON orders(receipt_number);
-- Inventory queries
CREATE INDEX idx_inventory_levels_variant_location
ON inventory_levels(variant_id, location_id);
CREATE INDEX idx_inventory_levels_location_reorder
ON inventory_levels(location_id) WHERE on_hand <= reorder_point;
-- Product search
CREATE INDEX idx_products_sku ON products(sku);
CREATE INDEX idx_product_variants_barcode ON product_variants(barcode);
CREATE INDEX idx_products_search ON products USING gin(to_tsvector('english', name));
-- Customer lookup
CREATE INDEX idx_customers_email ON customers(lower(email));
CREATE INDEX idx_customers_phone ON customers(phone);
CREATE INDEX idx_customers_search ON customers
USING gin(to_tsvector('english', first_name || ' ' || last_name));
-- Event sourcing
CREATE INDEX idx_domain_events_aggregate ON domain_events(aggregate_type, aggregate_id);
CREATE INDEX idx_domain_events_created ON domain_events(created_at);
-- Audit trail
CREATE INDEX idx_audit_logs_entity ON audit_logs(entity_type, entity_id);
CREATE INDEX idx_audit_logs_user ON audit_logs(performed_by);
CREATE INDEX idx_audit_logs_time ON audit_logs(created_at DESC);
-- RFID
CREATE UNIQUE INDEX idx_rfid_tags_epc ON rfid_tags(epc);
CREATE INDEX idx_rfid_tags_variant ON rfid_tags(variant_id);
-- Orders partitioned by month
CREATE TABLE orders (
id UUID,
created_at TIMESTAMP,
-- other columns
) PARTITION BY RANGE (created_at);
CREATE TABLE orders_2025_01 PARTITION OF orders
FOR VALUES FROM ('2025-01-01') TO ('2025-02-01');
CREATE TABLE orders_2025_02 PARTITION OF orders
FOR VALUES FROM ('2025-02-01') TO ('2025-03-01');
-- etc.
-- Domain events partitioned by month
CREATE TABLE domain_events (
id UUID,
created_at TIMESTAMP,
-- other columns
) PARTITION BY RANGE (created_at);
-- Audit logs partitioned by month
CREATE TABLE audit_logs (
id UUID,
created_at TIMESTAMP,
-- other columns
) PARTITION BY RANGE (created_at);
Table Columns Purpose
tenants subdomain Unique tenant subdomain
locations code Unique location code per tenant
users email Unique user email per tenant
products sku Unique SKU per tenant
product_variants sku Unique variant SKU per tenant
product_variants barcode Unique barcode per tenant
orders order_number Unique order number per tenant
orders receipt_number Unique receipt per tenant
customers customer_number Unique customer ID per tenant
gift_cards card_number Unique card number per tenant
rfid_tags epc Globally unique EPC
inventory_levels variant_id, location_id One record per variant-location
-- Positive quantities
ALTER TABLE inventory_levels ADD CONSTRAINT chk_on_hand_positive
CHECK (on_hand >= 0);
ALTER TABLE order_line_items ADD CONSTRAINT chk_quantity_positive
CHECK (quantity > 0);
-- Valid percentages
ALTER TABLE order_discounts ADD CONSTRAINT chk_discount_valid
CHECK (discount_value >= 0 AND discount_value <= 100);
-- Valid statuses
ALTER TABLE orders ADD CONSTRAINT chk_order_status
CHECK (status IN ('pending', 'completed', 'voided', 'refunded'));
-- Balance constraints
ALTER TABLE gift_cards ADD CONSTRAINT chk_balance_not_negative
CHECK (current_balance >= 0);
-- Tenant status
CREATE TYPE tenant_status AS ENUM ('active', 'suspended', 'trial', 'cancelled');
-- Location type
CREATE TYPE location_type AS ENUM ('store', 'warehouse', 'popup', 'mobile');
-- User role
CREATE TYPE user_role AS ENUM ('super_admin', 'admin', 'manager', 'cashier', 'viewer');
-- Order status
CREATE TYPE order_status AS ENUM ('pending', 'completed', 'voided', 'refunded');
-- Payment method
CREATE TYPE payment_method AS ENUM ('cash', 'card', 'gift_card', 'loyalty', 'other');
-- Payment status
CREATE TYPE payment_status AS ENUM ('pending', 'approved', 'declined', 'refunded');
-- Inventory transaction type
CREATE TYPE inv_transaction_type AS ENUM (
'sale', 'return', 'adjustment', 'transfer_out', 'transfer_in', 'receipt', 'shrinkage'
);
-- Cash movement type
CREATE TYPE cash_movement_type AS ENUM (
'till_drop', 'pickup', 'paid_in', 'paid_out', 'float_adjust'
);
-- RFID tag status
CREATE TYPE rfid_status AS ENUM ('active', 'sold', 'returned', 'void', 'lost');
-- Sync direction
CREATE TYPE sync_direction AS ENUM ('push', 'pull');
This ERD represents the complete database schema for the POS Platform with 51 tables across 13 domains.