Schema

Flow Sense database tables and columns.

Edit on GitHub

Flow Sense tables

Reference for core transactional tables used by sales orders, vehicle loading sheets (VLS), delivery orders, and file attachments.

clients

ColumnNotes
idPrimary key (UUID v7)
name
address
phone
email
statusactive, inactive, or suspended (default active)
archived_at
created_at
updated_atAuto-updated on row change

users

ColumnNotes
idPrimary key (UUID v7)
client_idOwning client
name
phone_number
rolesuper_admin, admin, sr, dsr, or distributor
emailUnique (case-insensitive via lower(email))
usernameUnique (case-insensitive via lower(username))
password_hashNullable until password is set
statusactive, inactive, or invited (default active)
created_at
updated_atAuto-updated on row change

roles

RBAC role definitions. users.role matches roles.key (e.g. sr, admin).

ColumnNotes
idPrimary key (UUID v7)
keyUnique (case-insensitive) role key
nameDisplay name
description
created_at
updated_atAuto-updated on row change

permissions

One row per tRPC procedure (e.g. user:list, order:create).

ColumnNotes
idPrimary key (UUID v7)
keyUnique (case-insensitive) permission key
description
created_at
updated_atAuto-updated on row change

role_permissions

Junction table: which roles are allowed which permissions.

ColumnNotes
idPrimary key (UUID v7)
role_idReferences roles.id
permission_idReferences permissions.id
created_at

Unique on (role_id, permission_id).

client_third_party_apis

Per-client warehouse ERP API configuration (Chainlink or compatible). No foreign key on client_id; application code enforces client scope.

ColumnNotes
idPrimary key (UUID v7)
client_idOwning client (clients.id, not enforced in DB)
integration_typewarehouse_products_sync, warehouse_order_create, warehouse_sales_orders_sync, or warehouse_delivery_confirm
base_urlAPI host only (e.g. https://api-chainlink-test.mudidokan.store); tower appends /api/v1/third-party/...
api_keyValue for X-API-Key / x-api-key header
warehouse_codeRequired for warehouse_products_sync; null for other integration types
enabledWhen true, integration is active
created_at
updated_atAuto-updated on row change

products

ColumnNotes
idPrimary key (UUID v7, Flow Sense)
external_product_idProduct id from warehouse ERP; nullable until first sync
client_idOwning client
warehouse_idERP warehouse id (text); scope for sync and uniqueness
product_name
brand_name
barcode
product_codeSKU / product code from ERP
unit_price
minimum_order_quantity
image_url
is_activetrue when present in latest sync; false when dropped
created_at
updated_atAuto-updated on row change

shops

ColumnNotes
idPrimary key
client_id
name
address
owner_name
owner_phone
lat
long
geomPostGIS point
ps_ref
front_image_urlOptional storefront image URL
archived_at
created_at
updated_at

shop_change_requests

Staging table for shop create/edit requests that require admin approval before changes are applied to shops.

Check: action = create requires shop_id null and route_id set; action = update requires shop_id set and route_id null.

Unique: at most one pending update per shop_id.

ColumnNotes
idPrimary key (UUID v7)
client_idOwning client
actioncreate or update
shop_idTarget shop for update; null for create
route_idIntended route for create (resolved server-side from today’s assignments and shop coordinates); null for update
payloadJSONB full proposed shop snapshot (name, address, owner_name, owner_phone, lat, long, ps_ref, front_image_url)
statuspending, approved, rejected, or cancelled (default pending)
requested_byUser who submitted the request
requested_atWhen the request was submitted
reviewed_byAdmin who approved or rejected; null until reviewed
reviewed_atWhen the request was reviewed; null until reviewed
review_noteRequired when rejecting; optional otherwise
result_shop_idshops.id created or updated on approval; null until approved
file_idsUUID array of linked files.id evidence photos; nullable (null or [] allowed)
created_at
updated_atAuto-updated on row change

On approval: create inserts a shops row from payload; update updates the target shop. shop_route_assignments is maintained by the existing shops geom trigger from lat / long.

SR file flow: call files.create first (phase shop-add-req or shop-edit-req, link.type shop_change_req — no parent id). Collect returned file ids, then pass fileIds on shop.createChangeRequest, shop.createUpdateChangeRequest, or shop.updateChangeRequest (full list replace). After link, files.get / files.edit scope via file_ids on this row (requester only). Unlinked shop-change files before submit are not visible via files.get.

routes

ColumnNotes
idPrimary key
client_id
name
tagp1 or p2
coordinatesGeoJSON polygon
geomPostGIS polygon
archived_at
created_at
created_by
updated_at
updated_by

shop_route_assignments

Junction table (auto-maintained by triggers): which shops fall inside which route polygons for a client.

ColumnNotes
shop_id
route_id
client_idDenormalized for client scoping
created_at
created_by
updated_at
updated_by

route_user_assignments

Which user is assigned to work a route on a given weekday.

ColumnNotes
idPrimary key (UUID v7)
route_id
client_id
assigned_user_idUser assigned to the route
dayWeekday: 1 = Sunday … 7 = Saturday
created_at
created_by
updated_at
updated_by

daily_shop_tasks

One row per shop visit by an SR. Visit date is created_at (calendar day in Asia/Dhaka). Which shops an SR should visit on a given weekday comes from route_user_assignments; route for a visit is derived via shop_idshop_route_assignmentsroutes.

Unique: at most one visit per (shop_id, created_by) per operating calendar day (created_at in Asia/Dhaka).

ColumnNotes
idPrimary key (UUID v7)
shop_idShop visited
statusinitial, open_no_order, closed, location_changed, not_found_at_location, destroyed, open_order_placed
note
latVisit proof latitude (snapshot at visit time; not the shop registry location)
longVisit proof longitude (snapshot at visit time)
geomPostGIS point; trigger-maintained from lat / long for spatial queries
created_atWhen the visit was recorded (visit date for filtering)
created_bySR who recorded the visit

orders

ColumnNotes
idPrimary key
client_id
shop_id
daily_shop_task_id
discountPercent discount (numeric, 0–100); nullable
created_byUser who takes the order
created_at
updated_at
updated_by
confirmed_at
confirmed_by
cancel_at
cancel_by
cancel_reason
warehouse_order_idWarehouse ERP order id after POST /api/v1/third-party/partner-orders
warehouse_order_statuse.g. pending from warehouse ERP
warehouse_order_submitted_atWhen warehouse order API accepted the order
warehouse_order_last_errorFull JSON partner-orders API error if submit failed

order_items

if unit_price = 0.00 and parent_product_id = null then the row represent a free product for the whole order

ColumnNotes
idPrimary key
order_id
product_id
parent_product_idNullable; free line for a paid product when unit_price is 0.00 and this is set
discountPercent discount on paid line (numeric, 0–100); nullable
order_quantity
unit_priceSnapshot of product price at order time

sales_orders

Synced from the warehouse ERP sales order API (warehouse_sales_orders_sync). Linked to a Flow Sense order via order_id = API external_ref. No foreign keys; application code enforces client scope.

ColumnNotes
idPrimary key (UUID v7)
client_idOwning client
order_idFlow Sense orders.id (same value as warehouse API external_ref)
warehouse_sales_order_idChainlink sale order id from GET /sale-orders response; used for delivery-confirmation API
discountPercent discount (numeric, 0–100); nullable; global order discount from Chainlink
statusWarehouse sale order status (see values below)
is_dsr_assignedfalse by default; set true when a DSR is assigned via VLS generation or salesOrder.assignUserForDelivery
created_by_idERP creator user id
created_by_nameERP creator display name
created_by_emailERP creator email
created_atERP created_at when synced; default now() on insert
rejected_by_idERP rejector user id; null if not rejected
rejected_by_nameERP rejector display name
rejected_by_emailERP rejector email
rejected_atERP rejection timestamp
updated_atAuto-updated on row change
updated_by

status values: draft, pending_availability_check, pending_finance_review, pending_approval, approved, sorting, dispatched, delivered, closed, cancelled.

Unique: (client_id, order_id).

sales_order_items

if unit_price_external = 0.00 and parent_product_id is set then the row is a line-bound free product; order-level free rows require Chainlink to send parent_product_id (null = global free)

ColumnNotes
idPrimary key (UUID v7)
sales_order_idParent sales_orders.id
product_idFlow Sense product id
parent_product_idNullable; paid product this free line belongs to when unit_price_external is 0.00
discountPercent discount on paid line (numeric, 0–100); nullable
sales_order_quantityDispatched quantity from warehouse ERP
unit_price_externalUnit price from warehouse ERP (unit_price)

trucks

Client-scoped truck registry. Used when generating a VLS; truck number is unique per client among non-archived rows.

ColumnNotes
idPrimary key (UUID v7)
client_idOwning client
truck_noTruck registration / fleet number
driver_nameDefault driver name; nullable
created_at
created_by
updated_atAuto-updated on row change
updated_by
archived_atSoft-delete timestamp; null when active

Unique: (client_id, lower(truck_no)) where archived_at is null.

vehicle_loading_sheets

VLS batch header. A client may have multiple sheets for the same work_date (partial generate, follow-up batches). Created when an admin finalizes shop-to-vehicle assignments.

ColumnNotes
idPrimary key (UUID v7)
client_idOwning client
work_dateOperating calendar date the VLS was built for (Asia/Dhaka)
delivery_datePlanned delivery date for generated delivery orders
created_at
created_byUser who generated the VLS
updated_atAuto-updated on row change
updated_by

Index: (client_id, work_date) — not unique; list all batches for a work date.

vls_vehicles

One row per truck/DSR slot on a VLS. Snapshots preserve truck and DSR details at generation time.

ColumnNotes
idPrimary key (UUID v7)
client_idOwning client
vls_idParent vehicle_loading_sheets.id
truck_idReferences trucks.id
assigned_user_idDSR assigned to this vehicle (users.id)
sequenceDisplay order of vehicles on the sheet (1-based)
labelUI label (e.g. Vehicle 1)
truck_no_snapshotTruck number at generation time
driver_name_snapshotDriver name at generation time; nullable
assigned_user_name_snapshotDSR display name at generation time
created_at

vls_vehicle_sales_orders

Junction table: which sales orders are loaded on which VLS vehicle, and in what stop order.

ColumnNotes
idPrimary key (UUID v7)
client_idOwning client
vls_vehicle_idParent vls_vehicles.id
sales_order_idsales_orders.id assigned to the vehicle
sort_orderStop sequence on the vehicle (1-based)
created_at

Unique: (client_id, sales_order_id) — each sales order can be on only one active VLS at a time. When an SO is released from a VLS, its row here is deleted; history is kept in vls_sales_order_assignment_events.

vls_sales_order_assignment_events

Audit log for VLS sales-order assignment changes. One row per add, release, or move. Rows are append-only (never deleted).

ColumnNotes
idPrimary key (UUID v7)
client_idOwning client
sales_order_idsales_orders.id affected
vls_idvehicle_loading_sheets.id involved
vls_vehicle_idvls_vehicles.id; nullable (e.g. release may not target one vehicle)
actionadded, released, or moved
reasonRequired free-text reason from the admin
performed_byusers.id who performed the action
performed_atWhen the action was recorded

action values:

  • added — SO placed on a VLS (generate or add-to-batch)
  • released — SO removed from a VLS; SO becomes available again if delivery allows
  • moved — release + add in one operation (target batch recorded on the event)

Index: (client_id, sales_order_id), (vls_id), (client_id, performed_at).

vls_vehicle_routes

Optimized driving route for a VLS vehicle (e.g. Google Directions). At most one route per vehicle.

ColumnNotes
idPrimary key (UUID v7)
client_idOwning client
vls_vehicle_idParent vls_vehicles.id
providerRoute provider key (e.g. google_directions)
origin_nameWarehouse / depot name
origin_latOrigin latitude
origin_lngOrigin longitude
overview_polylineEncoded route polyline; nullable
total_distance_metersTotal outbound distance
total_duration_secondsTotal outbound duration
return_distance_metersReturn leg distance to origin
return_duration_secondsReturn leg duration to origin
stopsJSON array of stop details (shop, coords, legs)
waypoint_orderJSON array of optimized waypoint indices
created_at
created_by
updated_atAuto-updated on row change
updated_by

Unique: (client_id, vls_vehicle_id).

delivery_orders

ColumnNotes
idPrimary key
client_id
assigned_to
order_id
sales_order_id
delivery_date
vls_vehicle_idSet when created via VLS generation; references vls_vehicles.id
bill_collection_statusnull (nothing collected yet), partial, or collected
completed_at
completed_by
canceled_at
canceled_by
cancel_reason
warehouse_delivery_confirmed_atWhen warehouse delivery-confirmation API accepted the delivery
warehouse_so_numberERP so_number from delivery-confirmation response
warehouse_delivered_atERP delivered_at from delivery-confirmation response
warehouse_delivery_last_errorFull JSON warehouse delivery-confirmation API error if confirm failed
created_at
updated_at
updated_by

bill_collection_status: null means no collection recorded yet; partial means some bill amount collected; collected means fully collected.

delivery_order_items

ColumnNotes
idPrimary key
delivery_order_id
product_id
delivered_quantity

delivery_order_tasks

ColumnNotes
idPrimary key
delivery_order_id
statusopen_no_delivery, closed, location_changed, not_found_at_location, destroyed, open_delivered
note
visited_at
lat
long
geom
created_at
created_by

bill_collections

ColumnNotes
idPrimary key
delivery_order_idDelivery order the payment applies to
user_idUser who recorded the collection
collected_amountPartial or full payment amount
created_at

files

Upload metadata for shop visits, deliveries, and shop change requests. Client scope is via junction tables for visit/delivery files, or via shop_change_requests.file_ids once linked (not client_id on this table).

ColumnNotes
idPrimary key
statuspending, queued, uploading, uploaded, missing, or failed (default pending)
phasetask_start, order, delivery, shop-add-req, or shop-edit-req
local_uri
url
kind
attempts
object_key
content_type
uploaded_at
created_at
updated_atAuto-updated on row change

daily_shop_task_files

Links a file to a shop visit (daily_shop_tasks). SR visit photos use this table; order-related photos on a visit also link here (use files.phase = order). Find files for an order via orders.daily_shop_task_id.

ColumnNotes
idPrimary key
daily_shop_task_id
file_id
created_at

delivery_order_task_files

ColumnNotes
idPrimary key
delivery_order_task_id
file_id
created_at

cash_collections

ColumnNotes
idPrimary key
collected_fromuser id from whom the cash is collected (basically DSR or SR)
collected_byuser id who collects the cash (basically warehouse manager)
expected_amount
collected_amount
note
dateYYYY-MM-DD input from client, not auto generated

bank_details

ColumnNotes
idPrimary key
client_id
account_typebank or mfs
namebank name or mfs name
account_number
account_holder_name
branchnull for mfs
routing_numbernull for mfs
mfs_account_typepersonal, agent, or merchant; null for bank
statusactive or inactive (default active)
created_at
created_by
updated_at
updated_by

cash_deposits

ColumnNotes
idPrimary key
deposited_byuser id who deposit the money
deposited_amount
adjusted_amountNullable; set when accounts adjusts the deposit
bank_id
deposit_referencebank slip no. or MFS transaction ID
cash_received_datedate of cash received
deposited_datewhen received amount will be deposited in the bank
depositor_note
statuspending, confirmed, cancelled, or adjusted
accounts_note
created_at
updated_at
updated_by

Last updated on

On this page