Skip to content

Database Schema (ERD)

The Merq platform uses a relational database schema in PostgreSQL to manage its core entities. The design follows a hierarchical structure, with all data being partitioned by a workspace_id.

The main data flow follows this progression: Principals have ProjectsProjects are assigned to TeamsTeams are assigned Outlets → Agents perform Visits to OutletsVisits generate Submissions.

This diagram illustrates the relationships between the most important tables in the database.

erDiagram
    Workspace {
        int id PK
        string name
    }

    User {
        int id PK
        int workspace_id FK
        int role_id FK
        string name
        string email
    }

    Role {
        int id PK
        int workspace_id FK
        string name
    }

    Principal {
        int id PK
        int workspace_id FK
        string name
    }

    Project {
        int id PK
        int workspace_id FK
        int principal_id FK
        string name
    }

    Team {
        int id PK
        int workspace_id FK
        string name
    }

    Outlet {
        int id PK
        int workspace_id FK
        int project_id FK
        string name
        string address
    }

    OutletVisit {
        int id PK
        int workspace_id FK
        int outlet_id FK
        int user_id FK
        datetime check_in_time
        datetime check_out_time
    }

    Product {
        int id PK
        int workspace_id FK
        string name
        string sku_code
    }

    Submission {
        int id PK
        int visit_id FK
        string type
        jsonb data
    }

    TeamUser {
        int team_id PK
        int user_id PK
    }

    ProjectTeam {
        int project_id PK
        int team_id PK
    }

    Workspace ||--o{ User : "has"
    Workspace ||--o{ Role : "has"
    Workspace ||--o{ Principal : "has"
    Workspace ||--o{ Project : "has"
    Workspace ||--o{ Team : "has"
    Workspace ||--o{ Outlet : "has"
    Workspace ||--o{ Product : "has"
    Workspace ||--o{ OutletVisit : "has"

    User }o--|| Role : "has"
    User }o--o{ TeamUser : "belongs to"
    User }o--o{ OutletVisit : "performs"

    Team ||--o{ TeamUser : "has"
    Team ||--o{ ProjectTeam : "assigned to"

    Principal ||--o{ Project : "has"

    Project }o--|| Principal : "belongs to"
    Project }o--o{ ProjectTeam : "has"
    Project }o--o{ Outlet : "contains"

    Outlet }o--|| Project : "belongs to"
    Outlet ||--o{ OutletVisit : "has"

    OutletVisit }o--o{ Submission : "generates"
  • Workspace: The top-level entity for data isolation. Every other major entity is directly or indirectly associated with a workspace_id. This is the cornerstone of the multi-tenant architecture.
  • User: Represents an individual user account. Each user belongs to one Workspace and is assigned one Role.
  • Role: Defines a set of permissions. Roles are workspace-specific.
  • Principal: Represents a brand or manufacturer whose products are being merchandised.
  • Project: A specific campaign or initiative for a Principal. This is a central organizing entity.
  • Team: A group of Users (field agents) who work on projects.
  • Outlet: A retail store or location that needs to be visited. Outlets are associated with a Project.
  • OutletVisit: Records a single visit by a User to an Outlet. It captures check-in/check-out times and location data.
  • Submission: Stores the data collected during an OutletVisit, such as stock counts, photos, or survey answers. The data field is a flexible jsonb column to accommodate different submission types.
  • Product: Represents a product that is tracked within the system.
  • TeamUser: A many-to-many join table linking Users to Teams. A user can be part of multiple teams, and a team has multiple users.
  • ProjectTeam: A many-to-many join table linking Projects to Teams. A project can be assigned to multiple teams, and a team can work on multiple projects.

These tables exist in the database but are omitted from the ERD above to keep the diagram focused on the core domain hierarchy.

TableDescription
employee_schedulesWeekly work schedule per employee (weekday, work_start/end, is_off_day).
employee_compensationsSalary metadata per employee (salary_band, pay_type, base_salary, currency). One-to-one with users.
employee_leave_entitlementsAnnual leave allocations per employee per year (annual, sick, carry-over days).
leave_requestsLeave/time-off requests with an approval workflow (pending → approved/rejected).
TableDescription
reimbursementsExpense reimbursement claims submitted by employees. Tracks category (transport, meal, fuel, etc.), amount, receipt photo, and an approval workflow (pending → approved/rejected). Optionally linked to an outlet or visit.
TableDescription
sales_ordersSales order records managed by field agents. See internal/domain for the full model.
TableDescription
formsDynamic forms created via the form builder. Used for custom submission types and surveys during outlet visits.
TableDescription
plansSubscription plans (e.g., Free, Basic, Pro).
plan_typesPlan type categories (free, basic, pro, enterprise).
plan_featuresFeatures that can be assigned to plans (e.g., “Unlimited Projects”).
plan_feature_assignmentsMany-to-many join between plans and plan_features.
plan_subscriptionsLinks a workspace to a plan.
plan_pricingsPricing rows per plan (amount, currency, interval: monthly/yearly).
plan_limitsResource limits imposed by a plan (e.g., max projects, max users).
plan_modulesModules controlled by plan limits (e.g., analytics, reporting).
TableDescription
locationsGeographic location records (cities, regions, etc.) used for outlet and user location data.
currenciesCurrency definitions used across reimbursements, plan pricing, and compensation.
TableDescription
notificationsIn-app and push notification records sent to users.
TableDescription
operating_schedulesWorkspace-level operating schedule definitions (e.g., business hours).
master_settingsKey-value settings store for workspace-level configuration.
login_auditsAudit log of user login events (timestamp, IP, device).
seeder_historiesTracks which seeder scripts have been run to prevent duplicate execution.
export_jobsAsync export job records (status, file URL, entity type).
import_jobsAsync import job records (status, validation results, entity type).