Skip to content

Database Schema

Overview

The system uses two separate PostgreSQL databases:

  • HQ Database (pos_hq): Master data + aggregated store data. Used by hq-server.
  • Store Database (pos_store): Local copy of master data + store-specific transactional data. Used by store-server. Each store has its own database instance.

Both databases share the same schema (defined via Drizzle ORM in apps/hq-server/src/db/schema/ and apps/store-server/src/db/schema/). The store database is populated through sync.

Numeric Conventions

All monetary amounts and quantities use integers to avoid floating-point errors:

TypeStorageExample
MoneyCents (integer)$12.99 = 1299
Tax ratesBasis points (integer)8.25% = 825
QuantitiesMultiplied by 1000 (integer)2.5 units = 2500

These conventions are enforced across the entire stack: database columns use bigint mode number, shared types use number, and the frontend formats them for display.

Schema Diagram

products ──────── departments
    |                  |
    +── tax_groups ── tax_group_rates ── tax_rates
    |
    +── product_suppliers ── suppliers
    |
    +── product_specials
    |
    +── store_product_dynamic ── stores
    |
    +── sale_items ── sales ── sale_payments
    |       |
    |       +── sale_item_taxes
    |
    +── inventory
    |
    +── inventory_adjustments
    |
    +── transfer_items ── transfers
    |
    +── purchase_order_items ── purchase_orders
    |
    +── worksheet_items ── worksheets ── worksheet_stores

users ── user_store_roles ── roles ── role_permissions ── permissions

sales_reps
customers
tenders
register_sessions
monthly_closings
sync_outbox / sync_status / sync_log / sequences

Table Reference

Products Module

products

ColumnTypeDescription
iduuid (PK)Auto-generated
skuvarchar(50)Unique stock keeping unit
barcodevarchar(100)Optional barcode
namevarchar(255)Product name
descriptiontextOptional description
department_iduuid (FK)Reference to departments
cost_pricebigintCost in cents
sell_pricebigintSell price in cents
tax_group_iduuid (FK)Reference to tax_groups
is_activebooleanSoft delete flag
track_stockbooleanWhether to track inventory
updated_attimestamptzLast modification
sync_versionintegerIncremented on each change for sync

departments

ColumnTypeDescription
iduuid (PK)Auto-generated
namevarchar(100)Department name
parent_iduuidSelf-referencing for hierarchy
sort_orderintegerDisplay order
updated_attimestamptzLast modification
sync_versionintegerSync tracking

tax_rates

ColumnTypeDescription
iduuid (PK)Auto-generated
namevarchar(100)Tax name (e.g., "State Tax")
rateintegerRate in basis points (825 = 8.25%)
is_compoundbooleanWhether this tax compounds on others
is_activebooleanActive flag
sync_versionintegerSync tracking

tax_groups

Groups multiple tax rates together. A product references a tax group, and the group contains the ordered list of rates to apply.

ColumnTypeDescription
iduuid (PK)Auto-generated
namevarchar(100)Group name (e.g., "Standard Tax")
descriptiontextOptional
is_activebooleanActive flag
sync_versionintegerSync tracking

tax_group_rates

Join table: which tax rates belong to which group, in what order.

ColumnTypeDescription
iduuid (PK)Auto-generated
tax_group_iduuid (FK)Reference to tax_groups
tax_rate_iduuid (FK)Reference to tax_rates
sort_orderintegerApplication order (compound taxes apply after earlier rates)

Unique constraint on (tax_group_id, tax_rate_id).

suppliers

ColumnTypeDescription
iduuid (PK)Auto-generated
namevarchar(255)Supplier name
contact_infotextContact details
payment_termstextPayment terms
sync_versionintegerSync tracking

product_suppliers

Many-to-many: which suppliers provide which products.

ColumnTypeDescription
iduuid (PK)Auto-generated
product_iduuid (FK)Reference to products
supplier_iduuid (FK)Reference to suppliers
supplier_skuvarchar(100)Supplier's SKU for this product
costbigintCost from this supplier in cents
is_preferredbooleanPreferred supplier flag
sort_orderintegerDisplay order

Unique constraint on (product_id, supplier_id).

product_specials

Time-limited special pricing for products.

ColumnTypeDescription
iduuid (PK)Auto-generated
product_iduuid (FK)Reference to products
special_pricebigintSpecial price in cents
start_datetimestamptzWhen the special starts
end_datetimestamptzWhen the special ends
descriptionvarchar(255)Optional description
is_activebooleanActive flag
sync_versionintegerSync tracking

Sales Module

sales

ColumnTypeDescription
iduuid (PK)Auto-generated
receipt_numbervarchar(50)Unique receipt number
cashier_iduuid (FK)Reference to users
terminal_idvarchar(20)Terminal identifier
statusvarchar(20)completed, voided, refunded, or parked
subtotalbigintSubtotal in cents
tax_totalbigintTotal tax in cents
discount_totalbigintTotal discount in cents
grand_totalbigintGrand total in cents
store_iduuidStore that created this sale
customer_iduuid (FK)Optional customer reference
sales_rep_iduuid (FK)Optional sales representative
created_attimestamptzSale timestamp
syncedbooleanWhether this sale has been synced to HQ

sale_items

ColumnTypeDescription
iduuid (PK)Auto-generated
sale_iduuid (FK)Reference to sales
product_iduuidProduct sold
product_namevarchar(255)Snapshot of product name at sale time
quantitybigintQuantity sold (x1000)
unit_pricebigintUnit price in cents
tax_rateintegerEffective tax rate in basis points
tax_amountbigintTax amount in cents
discount_amountbigintDiscount in cents
line_totalbigintLine total in cents
original_pricebigintPrice before any override
cost_pricebigintCost at time of sale
price_sourceinteger1=regular, 2=manual, 3=discount_percent, 4=discount_fixed, 5=promotion
stock_afterbigintStock level after this sale

sale_item_taxes

Breakdown of individual tax rates applied to each sale item.

ColumnTypeDescription
iduuid (PK)Auto-generated
sale_item_iduuid (FK)Reference to sale_items
tax_rate_namevarchar(100)Name of the tax rate
rateintegerRate in basis points
tax_amountbigintTax amount in cents
is_compoundbooleanWhether this was a compound tax
sort_orderintegerApplication order

sale_payments

ColumnTypeDescription
iduuid (PK)Auto-generated
sale_iduuid (FK)Reference to sales
methodvarchar(20)Tender code (e.g., CASH, CARD)
amountbigintPayment amount in cents
referencevarchar(255)Payment reference (e.g., card last 4 digits)

Users Module

users

ColumnTypeDescription
iduuid (PK)Auto-generated
usernamevarchar(50)Unique login name
pin_hashvarchar(255)bcrypt hash of PIN or password
display_namevarchar(100)Human-readable name
is_activebooleanActive flag
sync_versionintegerSync tracking

permissions

ColumnTypeDescription
iduuid (PK)Auto-generated
codevarchar(100)Unique permission code (e.g., products.view)
modulevarchar(50)Module name (e.g., products)
namevarchar(100)Human-readable name
descriptiontextDescription

roles

ColumnTypeDescription
iduuid (PK)Auto-generated
namevarchar(100)Role name (e.g., "Administrator")
descriptiontextRole description
is_systembooleanSystem roles cannot be deleted
sync_versionintegerSync tracking

role_permissions

Join table with composite PK (role_id, permission_id).

user_store_roles

Assigns roles to users, optionally scoped to a specific store.

ColumnTypeDescription
iduuid (PK)Auto-generated
user_iduuid (FK)Reference to users
store_iduuidNull = global (HQ-level) role
role_iduuid (FK)Reference to roles

Stores Module

stores

ColumnTypeDescription
iduuid (PK)Auto-generated
codevarchar(10)Unique store code (e.g., ST01)
namevarchar(255)Store name
addresstextPhysical address
is_activebooleanActive flag
last_sync_attimestamptzLast successful sync timestamp
sync_versionintegerSync tracking

store_product_dynamic

Per-store product data: local prices, stock levels, reorder points. Composite PK (store_id, product_id).

ColumnTypeDescription
store_iduuid (FK)Reference to stores
product_iduuid (FK)Reference to products
quantitybigintCurrent stock (x1000)
sell_pricebigintStore-specific sell price override (cents), null = use product default
cost_pricebigintStore-specific cost override (cents)
reorder_pointbigintReorder trigger level (x1000), default 10000
restock_levelbigintTarget restock level (x1000), default 50000
last_sold_attimestamptzLast sale of this product at this store
last_received_attimestamptzLast inventory receipt
sync_versionintegerSync tracking

Important: store_product_dynamic rows are created by the sync pull endpoint when a store first pulls products. They are never created directly by HQ operations.

purchase_orders

ColumnTypeDescription
iduuid (PK)Auto-generated
order_numbervarchar(50)Unique order number
supplier_iduuidSupplier
store_iduuidWhich store this PO is for
statusvarchar(20)draft, submitted, approved, sent, received, cancelled
totalbigintTotal in cents
notestextNotes
created_byuuid (FK)User who created
approved_byuuidUser who approved
sync_versionintegerSync tracking

purchase_order_items

ColumnTypeDescription
iduuid (PK)Auto-generated
purchase_order_iduuid (FK)Reference to purchase_orders
product_iduuid (FK)Reference to products
quantity_orderedbigintOrdered quantity (x1000)
quantity_receivedbigintReceived quantity (x1000)
unit_costbigintUnit cost in cents

Inventory Module

inventory

Simple stock tracking table. PK is product_id (one row per product).

ColumnTypeDescription
product_iduuid (PK/FK)Reference to products
quantitybigintCurrent stock level (x1000)
low_stock_thresholdbigintAlert threshold (x1000), default 10000

inventory_adjustments

ColumnTypeDescription
iduuid (PK)Auto-generated
product_iduuid (FK)Product adjusted
quantity_changebigintChange amount (x1000), positive or negative
reasonvarchar(20)Reason code (e.g., received, damaged, count)
reference_iduuidOptional reference (e.g., transfer ID)
created_byuuid (FK)User who made the adjustment
store_iduuidStore where adjustment occurred
syncedbooleanWhether synced to HQ

Transfers Module

transfers

ColumnTypeDescription
iduuid (PK)Auto-generated
from_store_iduuidSource store
to_store_iduuidDestination store
statusvarchar(30)draft, sent, received, cancelled
notestextNotes
created_byuuid (FK)User who created
sent_attimestamptzWhen shipped
received_attimestamptzWhen received
received_byuuid (FK)User who received
syncedbooleanWhether synced to HQ

transfer_items

ColumnTypeDescription
iduuid (PK)Auto-generated
transfer_iduuid (FK)Reference to transfers
product_iduuid (FK)Product transferred
quantity_sentbigintQuantity sent (x1000)
quantity_receivedbigintQuantity received (x1000)

Register Module

register_sessions

ColumnTypeDescription
iduuid (PK)Auto-generated
terminal_idvarchar(20)Terminal identifier
opened_byuuid (FK)User who opened
closed_byuuid (FK)User who closed
opening_amountbigintOpening cash in cents
closing_amountbigintClosing cash counted in cents
expected_amountbigintSystem-calculated expected cash
differencebigintVariance (closing - expected)
store_iduuidStore
opened_attimestamptzOpen timestamp
closed_attimestamptzClose timestamp
syncedbooleanWhether synced to HQ

monthly_closings

ColumnTypeDescription
iduuid (PK)Auto-generated
yearintegerYear
monthintegerMonth (1-12)
statusvarchar(10)open or closed
total_salesbigintTotal sales in cents
total_taxbigintTotal tax in cents
total_refundsbigintTotal refunds in cents
total_discountsbigintTotal discounts in cents
store_iduuidStore

Other Tables

customers

ColumnTypeDescription
iduuid (PK)Auto-generated
codevarchar(20)Unique customer code
first_namevarchar(100)First name
last_namevarchar(100)Last name
emailvarchar(255)Email
phonevarchar(30)Phone
tax_idvarchar(50)Tax identification number
addresstextAddress
notestextNotes
is_activebooleanActive flag
origin_store_iduuidStore where customer was created
sync_versionintegerSync tracking

tenders

Payment method types (e.g., Cash, Credit Card, Check).

ColumnTypeDescription
iduuid (PK)Auto-generated
codevarchar(20)Unique code (e.g., CASH)
namevarchar(100)Display name
typevarchar(20)Type category (e.g., cash, card, other)
is_activebooleanActive flag
sort_orderintegerDisplay order
opens_drawerbooleanWhether this tender opens the cash drawer
requires_referencebooleanWhether a reference number is required
sync_versionintegerSync tracking

sales_reps

ColumnTypeDescription
iduuid (PK)Auto-generated
codevarchar(20)Unique rep code
first_namevarchar(100)First name
last_namevarchar(100)Last name
phonevarchar(30)Phone
emailvarchar(255)Email
is_activebooleanActive flag
sync_versionintegerSync tracking

worksheets and worksheet_items

Worksheets support bulk product changes with an approval workflow: draft -> submitted -> approved -> applied (or rejected).

Types: new_product, price_change, product_update, tax_change, deactivate.

worksheet_stores

Which stores a worksheet applies to. Has an applied flag per store.

Sync Tables

See Sync System for detailed explanation.

sync_outbox (Store DB)

Transactional outbox for store-to-HQ push. Entries are created in the same transaction as the business operation, then a background process pushes them to HQ.

sync_status (HQ DB)

Aggregate counters per (store_id, entity_type, direction). Upserted on each sync operation. Never grows unbounded.

sync_log (HQ DB)

Detailed log of sync operations. Failed entries remain until resolved or retried from the HQ dashboard. Successful entries can be cleaned up.

sequences (Store DB)

Key-value store tracking the last sync version pulled for each entity type. Used by the pull service to request only changes since the last pull.

Migrations

Migrations are managed by Drizzle Kit:

bash
# HQ server migrations
pnpm --filter hq-server exec node --import tsx node_modules/drizzle-kit/bin.cjs generate

# Store server migrations
pnpm --filter store-server exec node --import tsx node_modules/drizzle-kit/bin.cjs generate

Migration files are stored in apps/hq-server/src/db/migrations/ and applied automatically on server startup.

sync_version Column

Most master data tables include a sync_version integer column. This is incremented every time a row is updated. The sync pull endpoint uses this to return only rows that changed since the store's last pull (WHERE sync_version > $sinceVersion). This avoids transferring the entire dataset on each sync cycle.