Files
ERP/NewERD.txt
T
ali_mohaghegh 633e9c2561 docs: add the current integrated ER export (NewERD.txt at repo root)
User-provided export of the final integrated procurement + inventory ER
(DBDatalyzer/dbdata-diagram format). Kept at the root as placed; the older
ERD lives in docs/NewERD.txt.
2026-09-03 15:01:16 +03:30

787 lines
12 KiB
Plaintext

////////////////////////////////////////////////////////////
// PROCUREMENT + INVENTORY SYSTEM
// FINAL INTEGRATED ER (FOR DBDATALYZER / DBDATADIAGRAM.IO)
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// ENUMS
////////////////////////////////////////////////////////////
enum quote_status {
offered
accepted
arrived
rejected
expired
}
enum termsheet_status {
offered
accepted
rejected
}
enum material_status {
active
discontinued
draft
}
enum attribute_data_type {
number
integer
string
enum
boolean
date
}
enum discount_type {
none
percent
fixed
}
enum item_decision_status {
pending
chosen
rejected
}
enum quote_event_type {
offered
accepted
rejected
arrived
note
edited
expired
}
enum inventory_transaction_reason {
PURCHASE
WITHDRAWAL
RETURN
TRANSFER_IN
TRANSFER_OUT
DAMAGE
WASTE
LOSS
FOUND
STOCK_COUNT
OTHER
}
////////////////////////////////////////////////////////////
// USERS
////////////////////////////////////////////////////////////
Table users {
id uuid [pk, default: `gen_random_uuid()`]
first_name text [not null]
last_name text [not null]
phone text
created_at timestamptz [
not null,
default: `now()`
]
}
////////////////////////////////////////////////////////////
// CATALOG
////////////////////////////////////////////////////////////
Table units {
id integer [pk]
code varchar [not null, unique]
name_fa varchar
dimension varchar
si_factor decimal [not null, default: 1.0]
is_base boolean [default: false]
}
Table brands {
id uuid [pk, default: `gen_random_uuid()`]
name text [not null]
country text
created_at timestamptz [
not null,
default: `now()`
]
indexes {
(name, country) [unique]
}
}
Table material_categories {
id uuid [pk, default: `gen_random_uuid()`]
slug text [not null, unique]
name text [not null]
description text
is_active boolean [
not null,
default: true
]
created_at timestamptz [
not null,
default: `now()`
]
updated_at timestamptz [
not null,
default: `now()`
]
}
Table attribute_definitions {
id uuid [pk, default: `gen_random_uuid()`]
attribute_key text [not null, unique]
label text [not null]
data_type attribute_data_type [not null]
unit_id integer [
ref: > units.id
]
enum_values jsonb
created_at timestamptz [
not null,
default: `now()`
]
}
Table category_attributes {
id uuid [pk, default: `gen_random_uuid()`]
category_id uuid [
not null,
ref: > material_categories.id
]
attribute_definition_id uuid [
not null,
ref: > attribute_definitions.id
]
is_required boolean [
not null,
default: false
]
is_filterable boolean [
not null,
default: false
]
conditional_rules jsonb
sort_order smallint [
not null,
default: 0
]
indexes {
(category_id, attribute_definition_id) [unique]
}
}
Table materials {
id uuid [pk, default: `gen_random_uuid()`]
category_id uuid [
not null,
ref: > material_categories.id
]
name text [not null]
// Main unit of the material.
// All inventory quantities are stored in this unit.
canonical_unit_id integer [
not null,
ref: > units.id
]
status material_status [
not null,
default: 'active'
]
created_at timestamptz [
not null,
default: `now()`
]
updated_at timestamptz [
not null,
default: `now()`
]
indexes {
(category_id, name) [unique]
}
}
Table brand_materials {
id uuid [pk, default: `gen_random_uuid()`]
brand_id uuid [
not null,
ref: > brands.id
]
material_id uuid [
not null,
ref: > materials.id
]
catalog_code text
is_active boolean [
not null,
default: true
]
created_at timestamptz [
not null,
default: `now()`
]
indexes {
(brand_id, material_id) [unique]
}
}
Table material_units {
id uuid [pk, default: `gen_random_uuid()`]
material_id uuid [
not null,
ref: > materials.id
]
// NULL = generic unit
// Non-NULL = brand-specific unit
brand_material_id uuid [
ref: > brand_materials.id
]
unit_id integer [
not null,
ref: > units.id
]
// Converts this unit to materials.canonical_unit_id
conversion_factor decimal(18,6) [
not null
]
is_default boolean [
not null,
default: false
]
indexes {
(material_id, brand_material_id, unit_id) [unique]
}
}
Table material_attribute_values {
id uuid [pk, default: `gen_random_uuid()`]
material_id uuid [
not null,
ref: > materials.id
]
attribute_definition_id uuid [
not null,
ref: > attribute_definitions.id
]
value_number decimal(18,6)
value_string text
value_boolean boolean
value_date date
indexes {
(material_id, attribute_definition_id) [unique]
}
}
////////////////////////////////////////////////////////////
// SUPPLIERS
////////////////////////////////////////////////////////////
Table suppliers {
id uuid [pk, default: `gen_random_uuid()`]
name text [not null]
location text
phone text
contact_person text
tax_id text
created_at timestamptz [
not null,
default: `now()`
]
}
////////////////////////////////////////////////////////////
// PROJECTS
////////////////////////////////////////////////////////////
Table projects {
id uuid [pk, default: `gen_random_uuid()`]
name text [not null]
description text
start_date date
created_at timestamptz [
not null,
default: `now()`
]
}
////////////////////////////////////////////////////////////
// QUOTATIONS
////////////////////////////////////////////////////////////
Table quotes {
id uuid [pk, default: `gen_random_uuid()`]
supplier_id uuid [
not null,
ref: > suppliers.id
]
project_id uuid [
ref: > projects.id
]
quote_date timestamptz [
not null,
default: `now()`
]
expiry_date date
accepted_date timestamptz
arrived_date timestamptz
status quote_status [
not null,
default: 'offered'
]
is_official boolean [
not null,
default: true
]
notes text
created_at timestamptz [
not null,
default: `now()`
]
updated_at timestamptz [
not null,
default: `now()`
]
}
Table quote_events {
id uuid [pk, default: `gen_random_uuid()`]
quote_id uuid [
not null,
ref: > quotes.id
]
event_type quote_event_type [
not null
]
payload jsonb [
not null,
default: '{}'
]
occurred_at timestamptz [
not null,
default: `now()`
]
actor text
}
////////////////////////////////////////////////////////////
// QUOTATION ITEMS
////////////////////////////////////////////////////////////
Table line_items {
id uuid [pk, default: `gen_random_uuid()`]
quote_id uuid [
not null,
ref: > quotes.id
]
material_id uuid [
not null,
ref: > materials.id
]
brand_material_id uuid [
ref: > brand_materials.id
]
unit_id integer [
not null,
ref: > units.id
]
// Ordered / requested quantity
quantity decimal(18,4) [
not null
]
notes text
created_at timestamptz [
not null,
default: `now()`
]
}
Table line_item_specs {
id uuid [pk, default: `gen_random_uuid()`]
line_item_id uuid [
not null,
ref: > line_items.id
]
attribute_definition_id uuid [
not null,
ref: > attribute_definitions.id
]
value_number decimal(18,6)
value_string text
value_boolean boolean
value_date date
unit_id_at_time integer [
ref: > units.id
]
indexes {
(line_item_id, attribute_definition_id) [unique]
}
}
////////////////////////////////////////////////////////////
// TERMSHEETS
////////////////////////////////////////////////////////////
Table termsheets {
id uuid [pk, default: `gen_random_uuid()`]
quote_id uuid [
not null,
ref: > quotes.id
]
label text [not null]
status termsheet_status [
not null,
default: 'offered'
]
discount_type discount_type [
not null,
default: 'none'
]
discount_value decimal(18,4) [
not null,
default: 0
]
created_at timestamptz [
not null,
default: `now()`
]
}
Table termsheet_schedule_steps {
id uuid [pk, default: `gen_random_uuid()`]
termsheet_id uuid [
not null,
ref: > termsheets.id
]
step_no smallint [
not null
]
due_date date [
not null
]
amount decimal(18,2) [
not null
]
note text
indexes {
(termsheet_id, step_no) [unique]
}
}
////////////////////////////////////////////////////////////
// ITEM TERMSHEETS
////////////////////////////////////////////////////////////
Table item_termsheets {
id uuid [pk, default: `gen_random_uuid()`]
line_item_id uuid [
not null,
ref: > line_items.id
]
termsheet_id uuid [
not null,
ref: > termsheets.id
]
unit_price decimal(18,4) [
not null
]
min_order_qty decimal(18,4)
total_price decimal(18,2)
indexes {
(line_item_id, termsheet_id) [unique]
}
}
////////////////////////////////////////////////////////////
// ITEM DECISIONS
////////////////////////////////////////////////////////////
Table item_decisions {
id uuid [pk, default: `gen_random_uuid()`]
line_item_id uuid [
not null,
unique,
ref: - line_items.id
]
chosen_item_termsheet_id uuid [
ref: > item_termsheets.id
]
status item_decision_status [
not null,
default: 'pending'
]
adjusted_unit_price decimal(18,4)
adjusted_notes text
decided_at timestamptz
}
////////////////////////////////////////////////////////////
// WAREHOUSES
////////////////////////////////////////////////////////////
Table warehouses {
id uuid [pk, default: `gen_random_uuid()`]
project_id uuid [
not null,
ref: > projects.id
]
name text [not null]
description text
created_at timestamptz [
not null,
default: `now()`
]
}
////////////////////////////////////////////////////////////
// WAREHOUSE ACCESS
////////////////////////////////////////////////////////////
Table warehouse_users {
warehouse_id uuid [
not null,
ref: > warehouses.id
]
user_id uuid [
not null,
ref: > users.id
]
indexes {
(warehouse_id, user_id) [pk]
}
}
////////////////////////////////////////////////////////////
// CURRENT INVENTORY SNAPSHOT
////////////////////////////////////////////////////////////
Table inventory {
id uuid [pk, default: `gen_random_uuid()`]
warehouse_id uuid [
not null,
ref: > warehouses.id
]
material_id uuid [
not null,
ref: > materials.id
]
brand_id uuid [
ref: > brands.id
]
item_title text [not null]
specs jsonb [default: '{}']
// SHA256 of sorted canonical specs JSON (or 'default' if empty)
specs_hash varchar(64) [
not null,
default: 'default'
]
quantity decimal(18,6) [
not null,
default: 0
]
updated_at timestamptz [
not null,
default: `now()`
]
indexes {
(warehouse_id, material_id, brand_id, specs_hash) [unique]
(warehouse_id, item_title)
}
}
////////////////////////////////////////////////////////////
// INVENTORY TRANSACTIONS
////////////////////////////////////////////////////////////
Table inventory_transactions {
id uuid [pk, default: `gen_random_uuid()`]
inventory_id uuid [
not null,
ref: > inventory.id
]
warehouse_id uuid [
not null,
ref: > warehouses.id
]
line_item_id uuid [
ref: >? line_items.id
]
quantity decimal(18,6) [
not null
]
reason inventory_transaction_reason [
not null
]
performed_by uuid [
not null,
ref: > users.id
]
transaction_date timestamptz [
not null,
default: `now()`
]
note text
created_at timestamptz [
not null,
default: `now()`
]
indexes {
(inventory_id, transaction_date)
(warehouse_id, transaction_date)
(inventory_id, line_item_id)
}
}