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.