From 633e9c2561f034c5505305ae7901182047e5d99f Mon Sep 17 00:00:00 2001 From: ali_mohaghegh Date: Thu, 3 Sep 2026 15:01:16 +0330 Subject: [PATCH] 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. --- NewERD.txt | 787 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 787 insertions(+) create mode 100644 NewERD.txt diff --git a/NewERD.txt b/NewERD.txt new file mode 100644 index 0000000..af89e0a --- /dev/null +++ b/NewERD.txt @@ -0,0 +1,787 @@ +//////////////////////////////////////////////////////////// +// 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) + } +} \ No newline at end of file