cpm-project/
├─ dist/
│  └─ js/
│     └─ cpm_esm.min.js
└─ src/
   ├─ cpm.js
   ├─ core/
   │  ├─ state.js
   │  ├─ events.js
   │  ├─ registry.js
   │  ├─ config.js
   │  └─ init.js
   ├─ data/
   │  ├─ project.js
   │  ├─ task.js
   │  ├─ dependency.js
   │  ├─ calendar.js
   │  └─ wbs.js
   ├─ resources/
   │  ├─ index.js
   │  ├─ human.js
   │  ├─ equipment.js
   │  ├─ material.js
   │  ├─ financial.js
   │  ├─ other.js
   │  └─ assignment.js
   ├─ scheduling/
   │  ├─ cpm.js
   │  ├─ network.js
   │  ├─ calendars.js
   │  └─ leveling.js
   ├─ cost/
   │  ├─ task_cost.js
   │  ├─ project_cost.js
   │  └─ cashflow.js
   ├─ ui/
   │  ├─ index.js
   │  ├─ layout.js
   │  ├─ task_table.js
   │  ├─ gantt.js
   │  ├─ resource_panel.js
   │  ├─ cost_panel.js
   │  ├─ toolbar.js
   │  ├─ status_bar.js
   │  └─ dialogs/
   │     ├─ task_editor.js
   │     ├─ resource_editor.js
   │     ├─ assignment_editor.js
   │     └─ settings_editor.js
   ├─ utils/
   │  ├─ dom.js
   │  ├─ date.js
   │  ├─ number.js
   │  ├─ validation.js
   │  └─ id.js
   └─ plugins/
      ├─ export_excel.js
      ├─ export_pdf.js
      └─ example_plugin.js
      
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

CREATE TABLE IF NOT EXISTS wp_cp_projects (
    id              BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    wp_post_id      BIGINT UNSIGNED NULL,
    code            VARCHAR(50) NOT NULL,
    name            VARCHAR(255) NOT NULL,
    description     TEXT NULL,
    start_date      DATE NOT NULL,
    end_date        DATE NULL,
    status          VARCHAR(50) DEFAULT 'planned',
    currency        VARCHAR(10) DEFAULT 'IRR',
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    UNIQUE KEY uq_wp_cp_projects_code (code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE IF NOT EXISTS wp_cp_calendars (
    id              BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    project_id      BIGINT UNSIGNED NOT NULL,
    name            VARCHAR(100) NOT NULL,
    description     TEXT NULL,
    type            VARCHAR(50) DEFAULT 'project',
    config_json     JSON NULL,
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    CONSTRAINT fk_wp_cp_calendars_project
        FOREIGN KEY (project_id) REFERENCES wp_cp_projects(id)
        ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE IF NOT EXISTS wp_cp_calendar_days (
    id              BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    calendar_id     BIGINT UNSIGNED NOT NULL,
    day_date        DATE NOT NULL,
    is_working      TINYINT(1) NOT NULL DEFAULT 1,
    note            VARCHAR(255) NULL,
    CONSTRAINT fk_wp_cp_calendar_days_calendar
        FOREIGN KEY (calendar_id) REFERENCES wp_cp_calendars(id)
        ON DELETE CASCADE,
    UNIQUE KEY uq_wp_cp_calendar_day (calendar_id, day_date)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE IF NOT EXISTS wp_cp_wbs (
    id              BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    project_id      BIGINT UNSIGNED NOT NULL,
    parent_id       BIGINT UNSIGNED NULL,
    code            VARCHAR(100) NOT NULL,
    name            VARCHAR(255) NOT NULL,
    sort_index      INT UNSIGNED NOT NULL DEFAULT 0,
    CONSTRAINT fk_wp_cp_wbs_project
        FOREIGN KEY (project_id) REFERENCES wp_cp_projects(id)
        ON DELETE CASCADE,
    CONSTRAINT fk_wp_cp_wbs_parent
        FOREIGN KEY (parent_id) REFERENCES wp_cp_wbs(id)
        ON DELETE SET NULL,
    UNIQUE KEY uq_wp_cp_wbs_project_code (project_id, code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE IF NOT EXISTS wp_cp_tasks (
    id              BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    project_id      BIGINT UNSIGNED NOT NULL,
    wbs_id          BIGINT UNSIGNED NULL,
    code            VARCHAR(50) NULL,
    name            VARCHAR(255) NOT NULL,
    description     TEXT NULL,
    duration        DECIMAL(10,2) NOT NULL,
    calendar_id     BIGINT UNSIGNED NULL,
    early_start     DATE NULL,
    early_finish    DATE NULL,
    late_start      DATE NULL,
    late_finish    DATE NULL,
    total_float     DECIMAL(10,2) NULL,
    is_critical     TINYINT(1) NOT NULL DEFAULT 0,
    status          VARCHAR(50) DEFAULT 'planned',
    progress        DECIMAL(5,2) DEFAULT 0.00,
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    CONSTRAINT fk_wp_cp_tasks_project
        FOREIGN KEY (project_id) REFERENCES wp_cp_projects(id)
        ON DELETE CASCADE,
    CONSTRAINT fk_wp_cp_tasks_wbs
        FOREIGN KEY (wbs_id) REFERENCES wp_cp_wbs(id)
        ON DELETE SET NULL,
    CONSTRAINT fk_wp_cp_tasks_calendar
        FOREIGN KEY (calendar_id) REFERENCES wp_cp_calendars(id)
        ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE IF NOT EXISTS wp_cp_dependencies (
    id                BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    project_id        BIGINT UNSIGNED NOT NULL,
    predecessor_id    BIGINT UNSIGNED NOT NULL,
    successor_id      BIGINT UNSIGNED NOT NULL,
    relation_type     VARCHAR(10) NOT NULL DEFAULT 'FS',
    lag               DECIMAL(10,2) NOT NULL DEFAULT 0,
    CONSTRAINT fk_wp_cp_dep_project
        FOREIGN KEY (project_id) REFERENCES wp_cp_projects(id)
        ON DELETE CASCADE,
    CONSTRAINT fk_wp_cp_dep_predecessor
        FOREIGN KEY (predecessor_id) REFERENCES wp_cp_tasks(id)
        ON DELETE CASCADE,
    CONSTRAINT fk_wp_cp_dep_successor
        FOREIGN KEY (successor_id) REFERENCES wp_cp_tasks(id)
        ON DELETE CASCADE,
    UNIQUE KEY uq_wp_cp_dep_pair (project_id, predecessor_id, successor_id, relation_type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE IF NOT EXISTS wp_cp_resources (
    id              BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    project_id      BIGINT UNSIGNED NOT NULL,
    type            VARCHAR(20) NOT NULL,
    code            VARCHAR(50) NULL,
    name            VARCHAR(255) NOT NULL,
    unit            VARCHAR(50) NULL,
    cost_per_unit   DECIMAL(18,4) NULL,
    currency        VARCHAR(10) DEFAULT 'IRR',
    calendar_id     BIGINT UNSIGNED NULL,
    attributes_json JSON NULL,
    is_active       TINYINT(1) NOT NULL DEFAULT 1,
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    CONSTRAINT fk_wp_cp_resources_project
        FOREIGN KEY (project_id) REFERENCES wp_cp_projects(id)
        ON DELETE CASCADE,
    CONSTRAINT fk_wp_cp_resources_calendar
        FOREIGN KEY (calendar_id) REFERENCES wp_cp_calendars(id)
        ON DELETE SET NULL,
    KEY idx_wp_cp_resources_type (type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE IF NOT EXISTS wp_cp_assignments (
    id              BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    project_id      BIGINT UNSIGNED NOT NULL,
    task_id         BIGINT UNSIGNED NOT NULL,
    resource_id     BIGINT UNSIGNED NOT NULL,
    quantity        DECIMAL(18,4) NOT NULL DEFAULT 0,
    cost_per_unit   DECIMAL(18,4) NULL,
    total_cost      DECIMAL(18,4) NULL,
    notes           VARCHAR(255) NULL,
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    CONSTRAINT fk_wp_cp_assign_project
        FOREIGN KEY (project_id) REFERENCES wp_cp_projects(id)
        ON DELETE CASCADE,
    CONSTRAINT fk_wp_cp_assign_task
        FOREIGN KEY (task_id) REFERENCES wp_cp_tasks(id)
        ON DELETE CASCADE,
    CONSTRAINT fk_wp_cp_assign_resource
        FOREIGN KEY (resource_id) REFERENCES wp_cp_resources(id)
        ON DELETE CASCADE,
    KEY idx_wp_cp_assign_task (task_id),
    KEY idx_wp_cp_assign_resource (resource_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE IF NOT EXISTS wp_cp_financial_items (
    id              BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    project_id      BIGINT UNSIGNED NOT NULL,
    task_id         BIGINT UNSIGNED NULL,
    category        VARCHAR(50) NOT NULL,
    description     VARCHAR(255) NULL,
    amount          DECIMAL(18,4) NOT NULL,
    currency        VARCHAR(10) DEFAULT 'IRR',
    trans_date      DATE NULL,
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    CONSTRAINT fk_wp_cp_fin_items_project
        FOREIGN KEY (project_id) REFERENCES wp_cp_projects(id)
        ON DELETE CASCADE,
    CONSTRAINT fk_wp_cp_fin_items_task
        FOREIGN KEY (task_id) REFERENCES wp_cp_tasks(id)
        ON DELETE SET NULL,
    KEY idx_wp_cp_fin_items_category (category)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE IF NOT EXISTS wp_cp_task_costs (
    id              BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    project_id      BIGINT UNSIGNED NOT NULL,
    task_id         BIGINT UNSIGNED NOT NULL,
    cost_resources  DECIMAL(18,4) NOT NULL DEFAULT 0,
    cost_financial  DECIMAL(18,4) NOT NULL DEFAULT 0,
    cost_total      DECIMAL(18,4) NOT NULL DEFAULT 0,
    currency        VARCHAR(10) DEFAULT 'IRR',
    last_calc_at    DATETIME NULL,
    CONSTRAINT fk_wp_cp_task_costs_project
        FOREIGN KEY (project_id) REFERENCES wp_cp_projects(id)
        ON DELETE CASCADE,
    CONSTRAINT fk_wp_cp_task_costs_task
        FOREIGN KEY (task_id) REFERENCES wp_cp_tasks(id)
        ON DELETE CASCADE,
    UNIQUE KEY uq_wp_cp_task_costs_task (task_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE IF NOT EXISTS wp_cp_project_costs (
    id                  BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    project_id          BIGINT UNSIGNED NOT NULL,
    cost_direct         DECIMAL(18,4) NOT NULL DEFAULT 0,
    cost_indirect       DECIMAL(18,4) NOT NULL DEFAULT 0,
    cost_overhead       DECIMAL(18,4) NOT NULL DEFAULT 0,
    cost_total          DECIMAL(18,4) NOT NULL DEFAULT 0,
    currency            VARCHAR(10) DEFAULT 'IRR',
    last_calc_at        DATETIME NULL,
    CONSTRAINT fk_wp_cp_project_costs_project
        FOREIGN KEY (project_id) REFERENCES wp_cp_projects(id)
        ON DELETE CASCADE,
    UNIQUE KEY uq_wp_cp_project_costs_project (project_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE IF NOT EXISTS wp_cp_cashflow (
    id              BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    project_id      BIGINT UNSIGNED NOT NULL,
    period_date     DATE NOT NULL,
    inflow          DECIMAL(18,4) NOT NULL DEFAULT 0,
    outflow         DECIMAL(18,4) NOT NULL DEFAULT 0,
    net_flow        DECIMAL(18,4) NOT NULL DEFAULT 0,
    currency        VARCHAR(10) DEFAULT 'IRR',
    CONSTRAINT fk_wp_cp_cashflow_project
        FOREIGN KEY (project_id) REFERENCES wp_cp_projects(id)
        ON DELETE CASCADE,
    UNIQUE KEY uq_wp_cp_cashflow_period (project_id, period_date)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

DELETE FROM wp_cp_cashflow;
DELETE FROM wp_cp_project_costs;
DELETE FROM wp_cp_task_costs;
DELETE FROM wp_cp_financial_items;
DELETE FROM wp_cp_assignments;
DELETE FROM wp_cp_resources;
DELETE FROM wp_cp_dependencies;
DELETE FROM wp_cp_tasks;
DELETE FROM wp_cp_wbs;
DELETE FROM wp_cp_calendar_days;
DELETE FROM wp_cp_calendars;
DELETE FROM wp_cp_projects;


------------------------------$_COOKIE
CREATE TABLE `wp_cp_task_dependencies` (
  `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
  `project_id` BIGINT(20) UNSIGNED NOT NULL,
  `predecessor_task_id` BIGINT(20) UNSIGNED NOT NULL,
  `successor_task_id` BIGINT(20) UNSIGNED NOT NULL,
  `type` ENUM('FS','FF','SS','SF') NOT NULL DEFAULT 'FS', -- Finish-Start etc.
  `lag` INT NOT NULL DEFAULT 0, -- تاخیر/تعجیل (مثبت/منفی) به واحد روز یا هر واحدی که سیستم تو استفاده می‌کند
  `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `project_id` (`project_id`),
  KEY `predecessor_task_id` (`predecessor_task_id`),
  KEY `successor_task_id` (`successor_task_id`),
  CONSTRAINT `fk_dep_project` FOREIGN KEY (`project_id`) REFERENCES `wp_cp_projects` (`id`) ON DELETE CASCADE,
  CONSTRAINT `fk_dep_predecessor` FOREIGN KEY (`predecessor_task_id`) REFERENCES `wp_cp_tasks` (`id`) ON DELETE CASCADE,
  CONSTRAINT `fk_dep_successor` FOREIGN KEY (`successor_task_id`) REFERENCES `wp_cp_tasks` (`id`) ON DELETE CASCADE,
  CONSTRAINT `uniq_dep_pair` UNIQUE (`project_id`, `predecessor_task_id`, `successor_task_id`, `type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-----------------------------------$_COOKIE

CREATE TABLE `wp_cp_task_assignments` (
  `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
  `project_id` BIGINT(20) UNSIGNED NOT NULL,
  `task_id` BIGINT(20) UNSIGNED NOT NULL,
  `resource_id` BIGINT(20) UNSIGNED NOT NULL,
  `allocation_percent` DECIMAL(5,2) NOT NULL DEFAULT 100.00, -- درصد مشارکت (مثلاً 50%)
  `units` DECIMAL(10,2) DEFAULT NULL,                        -- تعداد واحدها، ساعات، روزها، ...
  `role` VARCHAR(255) DEFAULT NULL,                          -- نقش (اختیاری، مثلاً "Engineer", "Manager")
  `notes` TEXT DEFAULT NULL,                                 -- توضیحات
  `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `project_id` (`project_id`),
  KEY `task_id` (`task_id`),
  KEY `resource_id` (`resource_id`),
  CONSTRAINT `fk_asg_project` FOREIGN KEY (`project_id`) REFERENCES `wp_cp_projects` (`id`) ON DELETE CASCADE,
  CONSTRAINT `fk_asg_task` FOREIGN KEY (`task_id`) REFERENCES `wp_cp_tasks` (`id`) ON DELETE CASCADE,
  CONSTRAINT `fk_asg_resource` FOREIGN KEY (`resource_id`) REFERENCES `wp_cp_resources` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

---------------------------------$_COOKIE
INSERT INTO wp_cp_projects (id, wp_post_id, code, name, description, start_date, end_date, status, currency)
VALUES
(1, NULL, 'PRJ-001', 'ساخت ساختمان اداری کوچک', 'پروژه نمونه برای سیستم CPM', '2025-01-01', '2025-04-30', 'planned', 'IRR');

INSERT INTO wp_cp_calendars (id, project_id, name, description, type, config_json)
VALUES
(1, 1, 'تقویم پروژه', 'شنبه تا چهارشنبه کاری، جمعه تعطیل', 'project',
 JSON_OBJECT('workdays', JSON_ARRAY(6, 0, 1, 2, 3), 'weekend', JSON_ARRAY(4,5))
);


INSERT INTO wp_cp_calendar_days (id, calendar_id, day_date, is_working, note)
VALUES
(1, 1, '2025-01-01', 0, 'تعطیل سال نو'),
(2, 1, '2025-03-21', 0, 'نوروز');

INSERT INTO wp_cp_wbs (id, project_id, parent_id, code, name, sort_index)
VALUES
(1, 1, NULL, '1.0', 'کل پروژه', 1);


INSERT INTO wp_cp_wbs (id, project_id, parent_id, code, name, sort_index)
VALUES
(2, 1, 1, '1.1', 'ساختمان اصلی', 1);

INSERT INTO wp_cp_tasks (
    id, project_id, wbs_id, code, name, description, duration,
    calendar_id, early_start, early_finish, late_start, late_finish,
    total_float, is_critical, status, progress
) VALUES
(1, 1, 2, 'T1', 'طراحی', 'طراحی معماری و سازه', 5.00,
 1, '2025-01-02', '2025-01-08', '2025-01-02', '2025-01-08',
 0.00, 1, 'planned', 0.00),


(2, 1, 2, 'T2', 'فونداسیون', 'اجرای فونداسیون', 10.00,
 1, '2025-01-09', '2025-01-20', '2025-01-09', '2025-01-20',
 0.00, 1, 'planned', 0.00),


(3, 1, 2, 'T3', 'اسکلت', 'اجرای اسکلت', 15.00,
 1, '2025-01-21', '2025-02-10', '2025-01-21', '2025-02-10',
 0.00, 1, 'planned', 0.00);

INSERT INTO wp_cp_dependencies (
    id, project_id, predecessor_id, successor_id, relation_type, lag
) VALUES
(1, 1, 1, 2, 'FS', 0.00),  -- T1 -> T2
(2, 1, 2, 3, 'FS', 0.00);  -- T2 -> T3

INSERT INTO wp_cp_resources (
    id, project_id, type, code, name, unit, cost_per_unit, currency, calendar_id, attributes_json, is_active
) VALUES

(1, 1, 'human', 'H-ENG', 'مهندس طراح', 'day', 1000000.0000, 'IRR', 1,
 JSON_OBJECT('role', 'Engineer', 'skill_level', 'Senior'), 1),


(2, 1, 'human', 'H-WKR', 'کارگر ساختمانی', 'day', 500000.0000, 'IRR', 1,
 JSON_OBJECT('role', 'Worker'), 1),


(3, 1, 'equipment', 'EQ-CRANE', 'جرثقیل', 'day', 2000000.0000, 'IRR', 1,
 JSON_OBJECT('capacity', '10t'), 1),


(4, 1, 'material', 'MAT-CONC', 'بتن', 'm3', 1500000.0000, 'IRR', NULL,
 JSON_OBJECT('grade', 'C30'), 1);

INSERT INTO wp_cp_assignments (
    id, project_id, task_id, resource_id, quantity, cost_per_unit, total_cost, notes
) VALUES

(1, 1, 1, 1, 5.0000, 1000000.0000, 5000000.0000, 'طراحی توسط مهندس'),


(2, 1, 2, 2, 10.0000, 500000.0000, 5000000.0000, 'کارگران برای فونداسیون'),


(3, 1, 2, 4, 50.0000, 1500000.0000, 75000000.0000, 'مصرف بتن برای فونداسیون'),


(4, 1, 3, 2, 15.0000, 500000.0000, 7500000.0000, 'کارگران برای اسکلت'),


(5, 1, 3, 3, 10.0000, 2000000.0000, 20000000.0000, 'استفاده از جرثقیل');

INSERT INTO wp_cp_financial_items (
    id, project_id, task_id, category, description, amount, currency, trans_date
) VALUES

(1, 1, NULL, 'indirect', 'هزینه‌های اداری و مدیریتی', 20000000.0000, 'IRR', '2025-02-01'),


(2, 1, NULL, 'tax', 'مالیات پروژه', 10000000.0000, 'IRR', '2025-03-01');

INSERT INTO wp_cp_task_costs (
    id, project_id, task_id, cost_resources, cost_financial, cost_total, currency, last_calc_at
) VALUES
(1, 1, 1, 5000000.0000, 0.0000, 5000000.0000, 'IRR', NOW()),

(2, 1, 2, 80000000.0000, 0.0000, 80000000.0000, 'IRR', NOW()),

(3, 1, 3, 27500000.0000, 0.0000, 27500000.0000, 'IRR', NOW());

INSERT INTO wp_cp_project_costs (
    id, project_id, cost_direct, cost_indirect, cost_overhead, cost_total, currency, last_calc_at
) VALUES
(1, 1, 112500000.0000, 30000000.0000, 0.0000, 142500000.0000, 'IRR', NOW());

INSERT INTO wp_cp_cashflow (
    id, project_id, period_date, inflow, outflow, net_flow, currency
) VALUES
(1, 1, '2025-01-31', 0.0000, 30000000.0000, -30000000.0000, 'IRR'),
(2, 1, '2025-02-28', 0.0000, 40000000.0000, -40000000.0000, 'IRR');

SET FOREIGN_KEY_CHECKS = 1;


بر اساس معماری‌ای که تا الان تثبیت کردیم، یک اسکیما‌ی اولیه می‌دهم که همه بخش‌ها (پروژه، تسک، وابستگی، تقویم، WBS، منابع، تخصیص‌ها، هزینه‌ها و…) را پوشش بدهد و همه جداول با پیشوند:


```sql
wp_cp_
```

ساخته شوند.  
زبان: MySQL/MariaDB (سازگار با وردپرس).

> توجه: این اسکیما «هسته» است؛ حین توسعه ممکن است ستون‌های بیشتری به‌صورت تدریجی اضافه شوند، ولی ساختار کلی را ثابت نگه می‌داریم.

---

## کوئری ساخت همه جداول (DDL)

```sql
-- 1) پروژه‌ها
CREATE TABLE IF NOT EXISTS wp_cp_projects (
    id              BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    wp_post_id      BIGINT UNSIGNED NULL,           -- اگر به یک پست وردپرس وصل شود
    code            VARCHAR(50) NOT NULL,           -- کد پروژه
    name            VARCHAR(255) NOT NULL,          -- نام پروژه
    description     TEXT NULL,
    start_date      DATE NOT NULL,
    end_date        DATE NULL,
    status          VARCHAR(50) DEFAULT 'planned',  -- planned, running, completed, on_hold, ...
    currency        VARCHAR(10) DEFAULT 'IRR',
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    UNIQUE KEY uq_wp_cp_projects_code (code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

------------------------------------------------------------
-- 2) تقویم‌ها
CREATE TABLE IF NOT EXISTS wp_cp_calendars (
    id              BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    project_id      BIGINT UNSIGNED NOT NULL,
    name            VARCHAR(100) NOT NULL,
    description     TEXT NULL,
    type            VARCHAR(50) DEFAULT 'project',  -- project, resource, ...
    config_json     JSON NULL,                      -- پیکربندی جزئی (مثلاً شیفت‌ها)
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    CONSTRAINT fk_wp_cp_calendars_project
        FOREIGN KEY (project_id) REFERENCES wp_cp_projects(id)
        ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

------------------------------------------------------------
-- 3) روزهای تقویم (مثلاً تعطیلات، تنظیمات خاص)
CREATE TABLE IF NOT EXISTS wp_cp_calendar_days (
    id              BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    calendar_id     BIGINT UNSIGNED NOT NULL,
    day_date        DATE NOT NULL,
    is_working      TINYINT(1) NOT NULL DEFAULT 1,  -- 1=کاری، 0=غیرکاری
    note            VARCHAR(255) NULL,
    CONSTRAINT fk_wp_cp_calendar_days_calendar
        FOREIGN KEY (calendar_id) REFERENCES wp_cp_calendars(id)
        ON DELETE CASCADE,
    UNIQUE KEY uq_wp_cp_calendar_day (calendar_id, day_date)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

------------------------------------------------------------
-- 4) WBS (ساختار شکست کار)
CREATE TABLE IF NOT EXISTS wp_cp_wbs (
    id              BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    project_id      BIGINT UNSIGNED NOT NULL,
    parent_id       BIGINT UNSIGNED NULL,
    code            VARCHAR(100) NOT NULL,           -- کد WBS (مثلاً 1.2.3)
    name            VARCHAR(255) NOT NULL,
    sort_index      INT UNSIGNED NOT NULL DEFAULT 0, -- برای مرتب‌سازی
    CONSTRAINT fk_wp_cp_wbs_project
        FOREIGN KEY (project_id) REFERENCES wp_cp_projects(id)
        ON DELETE CASCADE,
    CONSTRAINT fk_wp_cp_wbs_parent
        FOREIGN KEY (parent_id) REFERENCES wp_cp_wbs(id)
        ON DELETE SET NULL,
    UNIQUE KEY uq_wp_cp_wbs_project_code (project_id, code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

------------------------------------------------------------
-- 5) فعالیت‌ها (Tasks)
CREATE TABLE IF NOT EXISTS wp_cp_tasks (
    id              BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    project_id      BIGINT UNSIGNED NOT NULL,
    wbs_id          BIGINT UNSIGNED NULL,           -- اگر فعالیت زیر یک گره WBS باشد
    code            VARCHAR(50) NULL,               -- کد فعالیت (اگر لازم شد)
    name            VARCHAR(255) NOT NULL,
    description     TEXT NULL,
    duration        DECIMAL(10,2) NOT NULL,         -- مدت (روز یا واحد زمان پروژه)
    calendar_id     BIGINT UNSIGNED NULL,           -- تقویم خاص فعالیت (اگر داشت)
    -- تاریخ‌های برنامه‌ای (نتیجه CPM)
    early_start     DATE NULL,
    early_finish    DATE NULL,
    late_start      DATE NULL,
    late_finish     DATE NULL,
    total_float     DECIMAL(10,2) NULL,
    is_critical     TINYINT(1) NOT NULL DEFAULT 0,
    -- وضعیت
    status          VARCHAR(50) DEFAULT 'planned',  -- planned, in_progress, completed, ...
    progress        DECIMAL(5,2) DEFAULT 0.00,      -- درصد پیشرفت
    -- متادیتا
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    CONSTRAINT fk_wp_cp_tasks_project
        FOREIGN KEY (project_id) REFERENCES wp_cp_projects(id)
        ON DELETE CASCADE,
    CONSTRAINT fk_wp_cp_tasks_wbs
        FOREIGN KEY (wbs_id) REFERENCES wp_cp_wbs(id)
        ON DELETE SET NULL,
    CONSTRAINT fk_wp_cp_tasks_calendar
        FOREIGN KEY (calendar_id) REFERENCES wp_cp_calendars(id)
        ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

------------------------------------------------------------
-- 6) روابط پیش‌نیازی (Dependencies)
CREATE TABLE IF NOT EXISTS wp_cp_dependencies (
    id                BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    project_id        BIGINT UNSIGNED NOT NULL,
    predecessor_id    BIGINT UNSIGNED NOT NULL,
    successor_id      BIGINT UNSIGNED NOT NULL,
    relation_type     VARCHAR(10) NOT NULL DEFAULT 'FS',  -- FS, SS, FF, SF
    lag               DECIMAL(10,2) NOT NULL DEFAULT 0,   -- Lag (روز)
    CONSTRAINT fk_wp_cp_dep_project
        FOREIGN KEY (project_id) REFERENCES wp_cp_projects(id)
        ON DELETE CASCADE,
    CONSTRAINT fk_wp_cp_dep_predecessor
        FOREIGN KEY (predecessor_id) REFERENCES wp_cp_tasks(id)
        ON DELETE CASCADE,
    CONSTRAINT fk_wp_cp_dep_successor
        FOREIGN KEY (successor_id) REFERENCES wp_cp_tasks(id)
        ON DELETE CASCADE,
    UNIQUE KEY uq_wp_cp_dep_pair (project_id, predecessor_id, successor_id, relation_type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

------------------------------------------------------------
-- 7) منابع (Resources)
CREATE TABLE IF NOT EXISTS wp_cp_resources (
    id              BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    project_id      BIGINT UNSIGNED NOT NULL,
    type            VARCHAR(20) NOT NULL,      -- human, equipment, material, financial, other
    code            VARCHAR(50) NULL,          -- کد منبع
    name            VARCHAR(255) NOT NULL,
    unit            VARCHAR(50) NULL,          -- واحد (hour, day, m3, ...)
    cost_per_unit   DECIMAL(18,4) NULL,        -- هزینه واحد
    currency        VARCHAR(10) DEFAULT 'IRR',
    calendar_id     BIGINT UNSIGNED NULL,      -- تقویم خاص منبع
    attributes_json JSON NULL,                 -- مشخصات اضافی (مثلاً grade، capacity)
    is_active       TINYINT(1) NOT NULL DEFAULT 1,
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    CONSTRAINT fk_wp_cp_resources_project
        FOREIGN KEY (project_id) REFERENCES wp_cp_projects(id)
        ON DELETE CASCADE,
    CONSTRAINT fk_wp_cp_resources_calendar
        FOREIGN KEY (calendar_id) REFERENCES wp_cp_calendars(id)
        ON DELETE SET NULL,
    KEY idx_wp_cp_resources_type (type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

------------------------------------------------------------
-- 8) تخصیص منابع به فعالیت‌ها (Assignments)
CREATE TABLE IF NOT EXISTS wp_cp_assignments (
    id              BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    project_id      BIGINT UNSIGNED NOT NULL,
    task_id         BIGINT UNSIGNED NOT NULL,
    resource_id     BIGINT UNSIGNED NOT NULL,
    quantity        DECIMAL(18,4) NOT NULL DEFAULT 0,     -- مثلاً ساعت، روز، مقدار
    cost_per_unit   DECIMAL(18,4) NULL,                   -- اگر بخواهیم برای این assignment هزینه خاص بدهیم
    total_cost      DECIMAL(18,4) NULL,                   -- هزینه محاسبه‌شده، قابل کش
    notes           VARCHAR(255) NULL,
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    CONSTRAINT fk_wp_cp_assign_project
        FOREIGN KEY (project_id) REFERENCES wp_cp_projects(id)
        ON DELETE CASCADE,
    CONSTRAINT fk_wp_cp_assign_task
        FOREIGN KEY (task_id) REFERENCES wp_cp_tasks(id)
        ON DELETE CASCADE,
    CONSTRAINT fk_wp_cp_assign_resource
        FOREIGN KEY (resource_id) REFERENCES wp_cp_resources(id)
        ON DELETE CASCADE,
    KEY idx_wp_cp_assign_task (task_id),
    KEY idx_wp_cp_assign_resource (resource_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

------------------------------------------------------------
-- 9) آیتم‌های مالی مستقل (Financial Items)
CREATE TABLE IF NOT EXISTS wp_cp_financial_items (
    id              BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    project_id      BIGINT UNSIGNED NOT NULL,
    task_id         BIGINT UNSIGNED NULL,        -- اگر به یک فعالیت خاص مربوط باشد
    category        VARCHAR(50) NOT NULL,        -- direct, indirect, overhead, tax, ...
    description     VARCHAR(255) NULL,
    amount          DECIMAL(18,4) NOT NULL,
    currency        VARCHAR(10) DEFAULT 'IRR',
    trans_date      DATE NULL,                   -- تاریخ وقوع/پیش‌بینی
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    CONSTRAINT fk_wp_cp_fin_items_project
        FOREIGN KEY (project_id) REFERENCES wp_cp_projects(id)
        ON DELETE CASCADE,
    CONSTRAINT fk_wp_cp_fin_items_task
        FOREIGN KEY (task_id) REFERENCES wp_cp_tasks(id)
        ON DELETE SET NULL,
    KEY idx_wp_cp_fin_items_category (category)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

------------------------------------------------------------
-- 10) هزینه‌های فعالیت (Cache جدول هزینه‌ها – اختیاری)
CREATE TABLE IF NOT EXISTS wp_cp_task_costs (
    id              BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    project_id      BIGINT UNSIGNED NOT NULL,
    task_id         BIGINT UNSIGNED NOT NULL,
    cost_resources  DECIMAL(18,4) NOT NULL DEFAULT 0,   -- هزینه منابع (از assignments)
    cost_financial  DECIMAL(18,4) NOT NULL DEFAULT 0,   -- آیتم‌های مالی مرتبط
    cost_total      DECIMAL(18,4) NOT NULL DEFAULT 0,   -- جمع کل
    currency        VARCHAR(10) DEFAULT 'IRR',
    last_calc_at    DATETIME NULL,                      -- آخرین زمان محاسبه
    CONSTRAINT fk_wp_cp_task_costs_project
        FOREIGN KEY (project_id) REFERENCES wp_cp_projects(id)
        ON DELETE CASCADE,
    CONSTRAINT fk_wp_cp_task_costs_task
        FOREIGN KEY (task_id) REFERENCES wp_cp_tasks(id)
        ON DELETE CASCADE,
    UNIQUE KEY uq_wp_cp_task_costs_task (task_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

------------------------------------------------------------
-- 11) جمع‌بندی هزینه پروژه (Cache)
CREATE TABLE IF NOT EXISTS wp_cp_project_costs (
    id                  BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    project_id          BIGINT UNSIGNED NOT NULL,
    cost_direct         DECIMAL(18,4) NOT NULL DEFAULT 0,
    cost_indirect       DECIMAL(18,4) NOT NULL DEFAULT 0,
    cost_overhead       DECIMAL(18,4) NOT NULL DEFAULT 0,
    cost_total          DECIMAL(18,4) NOT NULL DEFAULT 0,
    currency            VARCHAR(10) DEFAULT 'IRR',
    last_calc_at        DATETIME NULL,
    CONSTRAINT fk_wp_cp_project_costs_project
        FOREIGN KEY (project_id) REFERENCES wp_cp_projects(id)
        ON DELETE CASCADE,
    UNIQUE KEY uq_wp_cp_project_costs_project (project_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

------------------------------------------------------------
-- 12) جریان نقدی (Cashflow) – در صورت استفاده
CREATE TABLE IF NOT EXISTS wp_cp_cashflow (
    id              BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    project_id      BIGINT UNSIGNED NOT NULL,
    period_date     DATE NOT NULL,                   -- مثلاً انتهای ماه
    inflow          DECIMAL(18,4) NOT NULL DEFAULT 0,
    outflow         DECIMAL(18,4) NOT NULL DEFAULT 0,
    net_flow        DECIMAL(18,4) NOT NULL DEFAULT 0,
    currency        VARCHAR(10) DEFAULT 'IRR',
    CONSTRAINT fk_wp_cp_cashflow_project
        FOREIGN KEY (project_id) REFERENCES wp_cp_projects(id)
        ON DELETE CASCADE,
    UNIQUE KEY uq_wp_cp_cashflow_period (project_id, period_date)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
```

---

### نکات مهم

1. **همه جداول با پیشوند `wp_cp_`** ساخته شده‌اند و با معماری ESM/Vanilla JS که قبلاً تعریف کردیم هم‌خوانی دارند.
2. `resources` یک جدول واحد با ستون `type` دارد (human, equipment, material, financial, other) تا با ماژول‌های `resources/*.js` هماهنگ باشد.
3. `assignments` دقیقاً نقش ماژول `resources/assignment.js` را در دیتابیس بازی می‌کند.
4. `financial_items` برای ماژول `resources/financial.js` و بخش `cost/` استفاده می‌شود.
5. `task_costs` و `project_costs` برای کش/گزارش هستند؛ می‌توانی اگر در فاز اول لازم نداری، موقتاً از استفاده‌شان صرف‌نظر کنی، ولی ساختار را ثابت نگه داریم.

اگر بخواهی، در مرحله بعد:

- می‌توانم برای هر جدول یک **INSERT نمونه** بدهم (نمونه داده پروژه کوچک)،  
یا
- یک **اسکریپت PHP/WordPress** آماده کنم که هنگام فعال‌سازی پلاگین، این جداول را با همین پیشوند `wp_cp_` ایجاد کند.

خیلی خوب؛ درخت کامل و یکپارچه را طوری می‌نویسم که بتوانی همین را به‌عنوان «مرجع معماری» نگه داری و در مسیر توسعه ساختار شاخه‌ها عوض نشود.  
همه چیز بر پایه Vanilla JS + ESM است و قابل خروجی گرفتن به یک فایل مثل `cpm_esm.min.js`.

درخت را از ریشه تا ریزترین زیرشاخه‌ها می‌دهم:

---

## ریشه پروژه

```text
cpm-project/
├─ package.json               # اگر برای build یا مدیریت وابستگی خواستی
├─ README.md
├─ dist/                      # خروجی نهایی (مثلاً cpm_esm.min.js)
│  └─ js/
│     └─ cpm_esm.min.js      # خروجی bundle/minify (مشابه tabulator_esm.min.js)
└─ src/                       # همه کدهای توسعه (ESM + Vanilla JS)
```

---

## ساختار کامل `src/`

```text
src/
├─ cpm.js                     # Entry اصلی (ESM) - API عمومی
├─ core/
├─ data/
├─ resources/
├─ scheduling/
├─ cost/
├─ ui/
├─ utils/
└─ plugins/
```

در ادامه، هر پوشه را با همه زیرشاخه‌ها کامل باز می‌کنم.

---

## 1. `src/core/` – هسته سیستم

```text
src/core/
├─ state.js                  # State مرکزی (project, tasks, resources, ...)
├─ events.js                 # Pub/Sub ساده برای رویدادها
├─ registry.js               # ثبت پلاگین‌ها / ماژول‌ها
├─ config.js                 # تنظیمات پیش‌فرض و قابل‌تغییر
└─ init.js                   # توابع اولیه‌سازی داخلی (با initCPM مرتبط است)
```

---

## 2. `src/data/` – مدل داده پروژه و فعالیت‌ها

```text
src/data/
├─ project.js                # ساخت/مدیریت اطلاعات پروژه
├─ task.js                   # CRUD فعالیت‌ها (Tasks)
├─ dependency.js             # روابط پیش‌نیازی (Dependencies)
├─ calendar.js               # تقویم کاری پروژه
└─ wbs.js                    # ساختار WBS (کدها و سلسله‌مراتب)
```

توضیح کوتاه:

- `project.js`: عنوان، کد، تاریخ شروع/پایان، توضیحات، وضعیت، ...
- `task.js`: فعالیت‌ها با فیلدهای مدت، تاریخ‌ها، شناسه‌ها، ...
- `dependency.js`: روابط FS/SS/FF/SF و Lag
- `calendar.js`: روزهای کاری، تعطیلات، شیفت‌ها
- `wbs.js`: مدیریت کدهای WBS و درخت ساختار پروژه

---

## 3. `src/resources/` – همه انواع منابع

```text
src/resources/
├─ index.js                  # ResourceRegistry – نقطه ورود
├─ human.js                  # نیروی انسانی
├─ equipment.js              # تجهیزات
├─ material.js               # مواد و مصالح
├─ financial.js              # آیتم‌های مالی (بودجه، هزینه‌ها، ...)
├─ other.js                  # منابع دیگر (Subcontract، سرویس‌ها، ...)
└─ assignment.js             # اتصال منابع به فعالیت‌ها (Resource Assignment)
```

- `index.js`:
  - addResource / getResource / removeResource …
- `human.js`:
  - ساخت، مدیریت و اختصاص نیروی انسانی
- `equipment.js`:
  - تجهیزاتو و اختصاصشان
- `material.js`:
  - مواد و مصرفشان روی فعالیت‌ها
- `financial.js`:
  - آیتم‌های هزینه/بودجه مستقل (مثلاً هزینه غیرمستقیم)
- `other.js`:
  - هر نوع خاص دیگر
- `assignment.js`:
  - جدول/لیست assignmentهای resource→task

---

## 4. `src/scheduling/` – برنامه‌ریزی، CPM، شبکه

```text
src/scheduling/
├─ cpm.js                    # محاسبه مسیر بحرانی (CPM)
├─ network.js                # ساخت گراف فعالیت‌ها، بررسی حلقه، ...
├─ calendars.js              # ابزار کار با تقویم (برای Scheduling)
└─ leveling.js               # (در آینده) Leveling منابع، اگر لازم شد
```

- `cpm.js`: محاسبه ES/EF/LS/LF/Float و Critical Path
- `network.js`: گراف و تحلیل شبکه
- `calendars.js`: ترکیب تقویم با محاسبات زمان
- `leveling.js`: رزرو برای آینده (Resource Leveling)

---

## 5. `src/cost/` – هزینه پروژه و فعالیت‌ها

```text
src/cost/
├─ task_cost.js              # محاسبه هزینه هر فعالیت از روی منابع + مالی
├─ project_cost.js           # محاسبه هزینه کل پروژه و گزارش‌ها
└─ cashflow.js               # (اختیاری) جریان نقدی در طول زمان
```

- `task_cost.js`:
  - جمع هزینه منابع اختصاص شده به فعالیت
- `project_cost.js`:
  - جمع کل، تفکیک direct/indirect/overhead
- `cashflow.js`:
  - اگر خواستی در زمان توسعه، جریان نقدی و نمودار Cash Flow اضافه شود

---

## 6. `src/ui/` – رابط کاربری (Vanilla JS + DOM)

```text
src/ui/
├─ index.js                  # نقطه ورود UI (renderCPM, refreshUI)
├─ layout.js                 # ایجاد اسکلت کلی صفحه (Layout)
├─ task_table.js             # جدول فعالیت‌ها (Task List / Grid)
├─ gantt.js                  # نمایش گانت (نمودار زمان‌بندی)
├─ resource_panel.js         # پانل منابع (لیست و مدیریت)
├─ cost_panel.js             # پانل هزینه (خلاصه‌ها و گزارش‌ها)
├─ toolbar.js                # نوار ابزار (دکمه‌ها: محاسبه، ذخیره، ...)
├─ status_bar.js             # نمایش وضعیت پروژه (info، وضعیت CPM، ...)
└─ dialogs/
   ├─ task_editor.js         # دیالوگ افزودن/ویرایش فعالیت
   ├─ resource_editor.js     # دیالوگ منابع
   ├─ assignment_editor.js   # دیالوگ اختصاص resource به task
   └─ settings_editor.js     # تنظیمات پروژه/سیستم
```

توضیح:

- `index.js`:
  - `renderCPM(rootElement, options)`
- `layout.js`:
  - ساخت DOM اصلی، تقسیم‌بندی بخش‌ها
- `task_table.js`:
  - جدول شبیه Tabulator ولی با Vanilla JS
- `gantt.js`:
  - رندر نمودار گانت (Canvas/SVG/DOM)
- `resource_panel.js`:
  - نمایش و مدیریت منابع
- `cost_panel.js`:
  - نمایش خلاصه هزینه، نمودار ساده
- `toolbar.js`:
  - دکمه‌های اصلی (محاسبه، ذخیره، چاپ، ...)
- `status_bar.js`:
  - اطلاعات وضعیت پروژه و CPM
- `dialogs/*`:
  - فرم‌های ویرایش/ایجاد

---

## 7. `src/utils/` – توابع کمکی عمومی

```text
src/utils/
├─ dom.js                    # کمک برای کار با DOM (ساخت، حذف، ...)
├─ date.js                   # کار با تاریخ/زمان (افزودن روز، فرمت، ...)
├─ number.js                 # فرمت اعداد، پول، واحدها
├─ validation.js             # اعتبارسنجی داده‌ها (tasks، deps، ...)
└─ id.js                     # تولید شناسه‌های یکتا (ID Generator)
```

- `dom.js`:
  - createElement, clearElement, addClass, removeClass, ...
- `date.js`:
  - addDays, diffDays, formatDate, parseDate, ...
- `number.js`:
  - formatCurrency, roundTo, ...
- `validation.js`:
  - جلوگیری از حلقه در dependencyها، چک داده‌های ورودی
- `id.js`:
  - تولید id برای task/resource/assignment

---

## 8. `src/plugins/` – پلاگین‌ها و توسعه‌های اختیاری

```text
src/plugins/
├─ export_excel.js           # (مثال) خروجی گرفتن جدول‌ها به Excel
├─ export_pdf.js             # (مثال) خروجی PDF گزارش‌ها
└─ example_plugin.js         # پلاگین نمونه (template برای توسعه‌های بعدی)
```

- این پوشه برای این است که در آینده بتوانی بدون دست‌زدن به هسته، امکانات جانبی اضافه کنی.

---

## 9. `src/cpm.js` – توضیح دقیق نقش این فایل

برای تأکید، `cpm.js` در ریشه `src/`:

```text
src/
└─ cpm.js
```

- تنها نقطه تماس با دنیای بیرون (مثلاً وردپرس)
- کارها:
  - مقداردهی `core/state`
  - صدا زدن `core/init`
  - صدا زدن `ui/index` برای render
  - آماده کردن APIهایی مثل:
    - `initCPM(rootElementId, options)`
    - `loadProject(projectData)`
    - `getCPMInstance()`

---

## نمای کلی نهایی (یکجا)

برای نگه‌داری به‌عنوان «Blueprint»، این کل درخت را یکجا می‌آورم:

```text
cpm-project/
├─ dist/
│  └─ js/
│     └─ cpm_esm.min.js
└─ src/
   ├─ cpm.js
   ├─ core/
   │  ├─ state.js
   │  ├─ events.js
   │  ├─ registry.js
   │  ├─ config.js
   │  └─ init.js
   ├─ data/
   │  ├─ project.js
   │  ├─ task.js
   │  ├─ dependency.js
   │  ├─ calendar.js
   │  └─ wbs.js
   ├─ resources/
   │  ├─ index.js
   │  ├─ human.js
   │  ├─ equipment.js
   │  ├─ material.js
   │  ├─ financial.js
   │  ├─ other.js
   │  └─ assignment.js
   ├─ scheduling/
   │  ├─ cpm.js
   │  ├─ network.js
   │  ├─ calendars.js
   │  └─ leveling.js
   ├─ cost/
   │  ├─ task_cost.js
   │  ├─ project_cost.js
   │  └─ cashflow.js
   ├─ ui/
   │  ├─ index.js
   │  ├─ layout.js
   │  ├─ task_table.js
   │  ├─ gantt.js
   │  ├─ resource_panel.js
   │  ├─ cost_panel.js
   │  ├─ toolbar.js
   │  ├─ status_bar.js
   │  └─ dialogs/
   │     ├─ task_editor.js
   │     ├─ resource_editor.js
   │     ├─ assignment_editor.js
   │     └─ settings_editor.js
   ├─ utils/
   │  ├─ dom.js
   │  ├─ date.js
   │  ├─ number.js
   │  ├─ validation.js
   │  └─ id.js
   └─ plugins/
      ├─ export_excel.js
      ├─ export_pdf.js
      └─ example_plugin.js
```

---

اگر همین درخت را تأیید کنی، در مراحل بعدی:

- روی همین ساختار ثابت می‌مانیم،
- فقط داخل فایل‌ها پیاده‌سازی را جلو می‌بریم،
- و هر جا لازم بود ساده‌سازی کنیم، **داخل فایل** این کار را می‌کنیم، نه با تغییر شاخه‌ها.

گام بعدی که پیشنهاد می‌کنم:  
یکی از دو مسیر را انتخاب کنیم تا با کد واقعی شروع کنیم:

1. شروع از `core/state.js` + `data/task.js`  
2. یا شروع از `resources/index.js` + `resources/human.js` + `resources/assignment.js`

بگو ترجیح می‌دهی پیاده‌سازی عملی را از کدام شاخه شروع کنیم تا همان را با کد واقعی (Vanilla JS + ESM) برایت بنویسم.



مستقیم می‌رم سراغ طراحی کامل REST API مطابق:

- ساختار state فعلی در Frontend (project, calendars, wbs, tasks, dependencies, resources, assignments, financial, cost, cashflow),
- و اسکیمای MySQL که خودت دادی (`wp_cp_*`).

فرض می‌گیرم backend در نهایت داخل یک پلاگین وردپرس (PHP) پیاده می‌شود، ولی فعلاً طراحی API را مستقل از تکنولوژی می‌نویسم (HTTP + JSON). بعداً اگر خواستی، می‌توانیم روی همین طراحی، کد PHP (یا Node, …) بنویسیم.

---

## 1. اصول کلی API

- Base URL (داخل وردپرس / یا هر سرور):  
  ` /wp-json/cpm/v1`  
  (اگر REST خارج از وردپرس است، مشابه `/api/cpm/v1`)

- همه مسیرها JSON برمی‌گردانند.
- همه مسیرها در نهایت روی جداول `wp_cp_*` می‌نشینند.
- فرمت JSON طوری طراحی می‌شود که Frontend بتواند همان را مستقیماً به شکل `projectData` مصرف کند.

---

## 2. مدل داده در Frontend (state)

ساختار کلی شیء `projectData` که Frontend انتظار دارد (قبلاً هم شبیه این را استفاده کردیم):

```js
{
  project: { ... },
  calendars: [ ... ],
  calendarDays: [ ... ],
  wbs: [ ... ],
  tasks: [ ... ],
  dependencies: [ ... ],
  resources: {
    human: [ ... ],
    equipment: [ ... ],
    material: [ ... ],
    other: [ ... ]
  },
  assignments: [ ... ],
  financialItems: [ ... ],
  taskCosts: [ ... ],
  projectCosts: [ ... ],
  cashflow: [ ... ]
}
```

طراحی API را طوری می‌چینم که:

- هم بتوانی یک endpoint تجمیع شده داشته باشی (برای تب کنترل پروژه در SPA),
- هم بتوانی بخش‌ها را جداگانه صدا بزنی (برای CRUDهای دقیق).

---

## 3. Endpoint تجمیع شده اصلی (برای Frontend CPM)

### 3.1. `GET /projects/{id}/full`

**هدف:** تمام داده یک پروژه را در قالبی که Frontend نیاز دارد برگرداند.

- **URL:**  
  `GET /wp-json/cpm/v1/projects/{project_id}/full`
- **Response 200 JSON:**

```json
{
  "project": {
    "id": 1,
    "code": "PRJ-001",
    "name": "ساخت ساختمان اداری کوچک",
    "description": "پروژه نمونه برای سیستم CPM",
    "startDate": "2025-01-01",
    "endDate": "2025-04-30",
    "status": "planned",
    "currency": "IRR"
  },
  "calendars": [
    {
      "id": 1,
      "projectId": 1,
      "name": "تقویم پروژه",
      "description": "شنبه تا چهارشنبه کاری، جمعه تعطیل",
      "type": "project",
      "config": {
        "workdays": [6, 0, 1, 2, 3],
        "weekend": [4, 5]
      }
    }
  ],
  "calendarDays": [
    {
      "id": 1,
      "calendarId": 1,
      "date": "2025-01-01",
      "isWorking": false,
      "note": "تعطیل سال نو"
    },
    {
      "id": 2,
      "calendarId": 1,
      "date": "2025-03-21",
      "isWorking": false,
      "note": "نوروز"
    }
  ],
  "wbs": [
    {
      "id": 1,
      "projectId": 1,
      "parentId": null,
      "code": "1.0",
      "name": "کل پروژه",
      "sortIndex": 1
    },
    {
      "id": 2,
      "projectId": 1,
      "parentId": 1,
      "code": "1.1",
      "name": "ساختمان اصلی",
      "sortIndex": 1
    }
  ],
  "tasks": [
    {
      "id": 1,
      "projectId": 1,
      "wbsId": 2,
      "code": "T1",
      "name": "طراحی",
      "description": "طراحی معماری و سازه",
      "duration": 5.0,
      "calendarId": 1,
      "earlyStart": "2025-01-02",
      "earlyFinish": "2025-01-08",
      "lateStart": "2025-01-02",
      "lateFinish": "2025-01-08",
      "totalFloat": 0.0,
      "isCritical": true,
      "status": "planned",
      "progress": 0.0
    }
    // ...
  ],
  "dependencies": [
    {
      "id": 1,
      "projectId": 1,
      "predecessorId": 1,
      "successorId": 2,
      "type": "FS",
      "lag": 0.0
    }
  ],
  "resources": {
    "human": [
      {
        "id": 1,
        "projectId": 1,
        "type": "human",
        "code": "H-ENG",
        "name": "مهندس طراح",
        "unit": "day",
        "costPerUnit": 1000000.0,
        "currency": "IRR",
        "calendarId": 1,
        "attributes": {
          "role": "Engineer",
          "skill_level": "Senior"
        },
        "isActive": true
      }
      // ...
    ],
    "equipment": [ ... ],
    "material": [ ... ],
    "other": [ ... ]
  },
  "assignments": [
    {
      "id": 1,
      "projectId": 1,
      "taskId": 1,
      "resourceId": 1,
      "quantity": 5.0,
      "costPerUnit": 1000000.0,
      "totalCost": 5000000.0,
      "notes": "طراحی توسط مهندس"
    }
  ],
  "financialItems": [
    {
      "id": 1,
      "projectId": 1,
      "taskId": null,
      "category": "indirect",
      "description": "هزینه‌های اداری و مدیریتی",
      "amount": 20000000.0,
      "currency": "IRR",
      "date": "2025-02-01"
    }
  ],
  "taskCosts": [
    {
      "id": 1,
      "projectId": 1,
      "taskId": 1,
      "costResources": 5000000.0,
      "costFinancial": 0.0,
      "costTotal": 5000000.0,
      "currency": "IRR",
      "lastCalcAt": "2025-02-01T10:00:00"
    }
  ],
  "projectCosts": {
    "id": 1,
    "projectId": 1,
    "costDirect": 112500000.0,
    "costIndirect": 30000000.0,
    "costOverhead": 0.0,
    "costTotal": 142500000.0,
    "currency": "IRR",
    "lastCalcAt": "2025-02-01T10:00:00"
  },
  "cashflow": [
    {
      "id": 1,
      "projectId": 1,
      "periodDate": "2025-01-31",
      "inflow": 0.0,
      "outflow": 30000000.0,
      "netFlow": -30000000.0,
      "currency": "IRR"
    }
  ]
}
```

فرانت‌اند می‌تواند این را مستقیم بدهد به:

- `loadProjectCore(project)`
- `loadCalendars(calendars, calendarDays)`
- `loadWbs(wbs)`
- `loadTasks(tasks)`
- `loadDependencies(dependencies)`
- `loadResourcesAndFinancial({ resources, assignments, financialItems, taskCosts, projectCosts, cashflow })`

### 3.2. `GET /projects` (لیست پروژه‌ها)

- **URL:**  
  `GET /wp-json/cpm/v1/projects`
- **Query params (اختیاری):**
  - `status` (planned, running, completed, …)
  - `search` (روی code/name)
- **Response 200:**

```json
[
  {
    "id": 1,
    "code": "PRJ-001",
    "name": "ساخت ساختمان اداری کوچک",
    "startDate": "2025-01-01",
    "endDate": "2025-04-30",
    "status": "planned",
    "currency": "IRR"
  }
]
```

### 3.3. `POST /projects` (ایجاد پروژه جدید)

- **URL:**  
  `POST /wp-json/cpm/v1/projects`
- **Body:**

```json
{
  "code": "PRJ-002",
  "name": "پروژه جدید",
  "description": "توضیحات",
  "startDate": "2025-05-01",
  "endDate": "2025-08-31",
  "status": "planned",
  "currency": "IRR",
  "wpPostId": 123
}
```

- **Response 201:**

```json
{
  "id": 2,
  "code": "PRJ-002",
  "name": "پروژه جدید",
  "startDate": "2025-05-01",
  "endDate": "2025-08-31",
  "status": "planned",
  "currency": "IRR"
}
```

### 3.4. `PUT /projects/{id}` و `DELETE /projects/{id}`

- استاندارد: update و delete پروژه (برای CRUD کامل).

---

## 4. Endpointهای جزئی (برای CRUD و ویرایش)

حالا هر ماژولی که در `src/` داری را به یک یا چند endpoint نگاشت می‌کنیم.

### 4.1. Calendars

#### `GET /projects/{id}/calendars`

- از `wp_cp_calendars` + `wp_cp_calendar_days`
- **Response:**

```json
{
  "calendars": [ ... ],
  "calendarDays": [ ... ]
}
```

#### `POST /projects/{id}/calendars`

برای ساخت تقویم جدید.

#### `PUT /calendars/{calendar_id}`  
#### `DELETE /calendars/{calendar_id}`

---

### 4.2. WBS

#### `GET /projects/{id}/wbs`

- از جدول `wp_cp_wbs`.
- Response:

```json
[
  {
    "id": 1,
    "projectId": 1,
    "parentId": null,
    "code": "1.0",
    "name": "کل پروژه",
    "sortIndex": 1
  }
]
```

#### `POST /projects/{id}/wbs`  
#### `PUT /wbs/{wbs_id}`  
#### `DELETE /wbs/{wbs_id}`

---

### 4.3. Tasks (فعالیت‌ها)

#### `GET /projects/{id}/tasks`

- از `wp_cp_tasks`
- Response:

```json
[
  {
    "id": 1,
    "projectId": 1,
    "wbsId": 2,
    "code": "T1",
    "name": "طراحی",
    "description": "طراحی معماری و سازه",
    "duration": 5.0,
    "calendarId": 1,
    "earlyStart": "2025-01-02",
    "earlyFinish": "2025-01-08",
    "lateStart": "2025-01-02",
    "lateFinish": "2025-01-08",
    "totalFloat": 0.0,
    "isCritical": true,
    "status": "planned",
    "progress": 0.0
  }
]
```

#### `POST /projects/{id}/tasks`

- بدنه:

```json
{
  "wbsId": 2,
  "code": "T4",
  "name": "نما",
  "description": "",
  "duration": 7.0,
  "calendarId": 1
}
```

#### `PUT /tasks/{task_id}`  
#### `DELETE /tasks/{task_id}`

---

### 4.4. Dependencies

#### `GET /projects/{id}/dependencies`

- از `wp_cp_dependencies`
- Response:

```json
[
  {
    "id": 1,
    "projectId": 1,
    "predecessorId": 1,
    "successorId": 2,
    "type": "FS",
    "lag": 0.0
  }
]
```

#### `POST /projects/{id}/dependencies`  
#### `DELETE /dependencies/{dep_id}`

---

### 4.5. Resources

#### `GET /projects/{id}/resources`

- از `wp_cp_resources`
- Response:

```json
{
  "human": [ ... ],
  "equipment": [ ... ],
  "material": [ ... ],
  "other": [ ... ]
}
```

ساختار هر منبع:

```json
{
  "id": 1,
  "projectId": 1,
  "type": "human",
  "code": "H-ENG",
  "name": "مهندس طراح",
  "unit": "day",
  "costPerUnit": 1000000.0,
  "currency": "IRR",
  "calendarId": 1,
  "attributes": { "role": "Engineer", "skill_level": "Senior" },
  "isActive": true
}
```

#### `POST /projects/{id}/resources`  
#### `PUT /resources/{resource_id}`  
#### `DELETE /resources/{resource_id}`

---

### 4.6. Assignments (تخصیص منابع)

#### `GET /projects/{id}/assignments`

- از `wp_cp_assignments`
- Response:

```json
[
  {
    "id": 1,
    "projectId": 1,
    "taskId": 1,
    "resourceId": 1,
    "quantity": 5.0,
    "costPerUnit": 1000000.0,
    "totalCost": 5000000.0,
    "notes": "طراحی توسط مهندس"
  }
]
```

#### `POST /projects/{id}/assignments`  
#### `PUT /assignments/{assignment_id}`  
#### `DELETE /assignments/{assignment_id}`

---

### 4.7. Financial Items

#### `GET /projects/{id}/financial-items`

- از `wp_cp_financial_items`
- Response:

```json
[
  {
    "id": 1,
    "projectId": 1,
    "taskId": null,
    "category": "indirect",
    "description": "هزینه‌های اداری و مدیریتی",
    "amount": 20000000.0,
    "currency": "IRR",
    "date": "2025-02-01"
  }
]
```

#### `POST /projects/{id}/financial-items`  
#### `PUT /financial-items/{id}`  
#### `DELETE /financial-items/{id}`

---

### 4.8. Task Costs & Project Costs (Cache)

معمولاً این‌ها را backend پس از محاسبه می‌نویسد؛ Frontend بیشتر خواننده است.

#### `GET /projects/{id}/task-costs`

- از `wp_cp_task_costs`

#### `GET /projects/{id}/project-costs`

- از `wp_cp_project_costs`

---

### 4.9. Cashflow

#### `GET /projects/{id}/cashflow`

- از `wp_cp_cashflow`

---

## 5. Endpointهای محاسبات (Scheduling / Costing) (اختیاری)

اگر می‌خواهی محاسبات CPM و هزینه را backend انجام دهد:

### `POST /projects/{id}/schedule/recalculate`

- backend:
  - داده‌ها را از `wp_cp_*` می‌خواند،
  - الگوریتم CPM (معادل `src/scheduling/cpm.js`) را اجرا می‌کند،
  - ES/EF/LS/LF/Float را در `wp_cp_tasks` به‌روز می‌کند،
  - پاسخ را بر می‌گرداند.

### `POST /projects/{id}/cost/recalculate`

- backend:
  - از `assignments`, `resources`, `financial_items` هزینه‌های task و project را محاسبه می‌کند،
  - جدول‌های `wp_cp_task_costs`, `wp_cp_project_costs`, `wp_cp_cashflow` را به‌روز می‌کند.

Frontend می‌تواند پس از این درخواست، دوباره `GET /projects/{id}/full` را بزند.

---

## 6. نقشه مستقیم Frontend ↔ API

برای تب «کنترل پروژه» در SPA:

1. هنگام باز شدن تب:
   - `GET /projects/{id}/full`
   - نتیجه → `Cpm.loadProjectData(projectData)`
2. برای ذخیره/ویرایش:
   - CRUD فرم‌ها:
     - Task editor → `POST/PUT /tasks/...`
     - Resource editor → `POST/PUT /resources/...`
     - Assignment editor → `POST/PUT /assignments/...`
3. برای محاسبه جدید برنامه:
   - دکمه «محاسبه برنامه» → `POST /projects/{id}/schedule/recalculate`
   - بعد از موفقیت → `GET /projects/{id}/full` → refresh UI.

---

## 7. قدم بعدی

اگر این طراحی را قبول داری، دو مسیر داریم:

1. من روی همین طراحی، برایت اسکلت کد backend (مثلاً PHP/WordPress REST API) بنویسم:
   - ثبت namespace `cpm/v1`،
   - routes اصلی (`/projects`, `/projects/{id}/full`, `/tasks`, ...)،
   - map هر route به queryهای `wp_cp_*`.
2. یا اگر backend را با Node/Express می‌نویسی، یک سرور Express با همین endpointها را طراحی کنیم.

به‌خاطر این‌که اسکیما و prefix `wp_cp_` واضحاً وردپرس‌محور است، اگر تایید کنی که backend را داخل پلاگین وردپرس می‌سازی، در پیام بعد:

- یک فایل PHP نمونه مثل `class-cpm-rest-controller.php` می‌نویسم،
- و حداقل دو endpoint کامل (`GET /projects/{id}/full` و `GET /projects`) را تا حد کد واقعی پیاده می‌کنم.



سطح دسترسی
